mirror of
				https://github.com/NginxProxyManager/nginx-proxy-manager.git
				synced 2025-11-04 01:15:14 +00:00 
			
		
		
		
	Compare commits
	
		
			47 Commits
		
	
	
		
			v2.12.1
			...
			8fb9d9fec6
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					8fb9d9fec6 | ||
| 
						 | 
					cea9a17218 | ||
| 
						 | 
					6bbe7d4cc4 | ||
| 
						 | 
					c6ab315165 | ||
| 
						 | 
					2c1595756d | ||
| 
						 | 
					b3de76c945 | ||
| 
						 | 
					fcf4117f8e | ||
| 
						 | 
					d26e8c1d0c | ||
| 
						 | 
					19ed4c1212 | ||
| 
						 | 
					03018d252b | ||
| 
						 | 
					8351dd41f6 | ||
| 
						 | 
					97212f2686 | ||
| 
						 | 
					fe068a8b51 | ||
| 
						 | 
					61e2bde98f | ||
| 
						 | 
					81c9038929 | ||
| 
						 | 
					4ea50ca40c | ||
| 
						 | 
					53ed12bcf2 | ||
| 
						 | 
					cb3e4ed59c | ||
| 
						 | 
					b20dc5eade | ||
| 
						 | 
					586afc0c91 | ||
| 
						 | 
					93ea17a9bb | ||
| 
						 | 
					2075f98cad | ||
| 
						 | 
					07a4e5791f | ||
| 
						 | 
					640a1eeb68 | ||
| 
						 | 
					20646e7bb5 | ||
| 
						 | 
					87998a03ce | ||
| 
						 | 
					a0b26b9e98 | ||
| 
						 | 
					d6791f4e38 | ||
| 
						 | 
					62c94f3099 | ||
| 
						 | 
					25a26d6175 | ||
| 
						 | 
					17246e418f | ||
| 
						 | 
					f7d3ca0b07 | ||
| 
						 | 
					a55de386e7 | ||
| 
						 | 
					e9d4f5b827 | ||
| 
						 | 
					1c1cee3836 | ||
| 
						 | 
					eaf6335694 | ||
| 
						 | 
					ffe05ebd41 | ||
| 
						 | 
					2e9a4f1aed | ||
| 
						 | 
					d17c85e4c8 | ||
| 
						 | 
					dad8d0ca00 | ||
| 
						 | 
					d7e0558a35 | ||
| 
						 | 
					ee41bb5562 | ||
| 
						 | 
					0cf6b9caa4 | ||
| 
						 | 
					68a9baf206 | ||
| 
						 | 
					d92421d098 | ||
| 
						 | 
					96c58b203e | ||
| 
						 | 
					d499e2bfef | 
							
								
								
									
										10
									
								
								Jenkinsfile
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										10
									
								
								Jenkinsfile
									
									
									
									
										vendored
									
									
								
							@@ -43,7 +43,7 @@ pipeline {
 | 
				
			|||||||
					steps {
 | 
										steps {
 | 
				
			||||||
						script {
 | 
											script {
 | 
				
			||||||
							// Defaults to the Branch name, which is applies to all branches AND pr's
 | 
												// Defaults to the Branch name, which is applies to all branches AND pr's
 | 
				
			||||||
							buildxPushTags = "-t docker.io/jc21/${IMAGE}:github-${BRANCH_LOWER}"
 | 
												buildxPushTags = "-t docker.io/nginxproxymanager/${IMAGE}-dev:${BRANCH_LOWER}"
 | 
				
			||||||
						}
 | 
											}
 | 
				
			||||||
					}
 | 
										}
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
@@ -203,7 +203,13 @@ pipeline {
 | 
				
			|||||||
					}
 | 
										}
 | 
				
			||||||
					steps {
 | 
										steps {
 | 
				
			||||||
						script {
 | 
											script {
 | 
				
			||||||
							npmGithubPrComment("Docker Image for build ${BUILD_NUMBER} is available on [DockerHub](https://cloud.docker.com/repository/docker/jc21/${IMAGE}) as `jc21/${IMAGE}:github-${BRANCH_LOWER}`\n\n**Note:** ensure you backup your NPM instance before testing this PR image! Especially if this PR contains database changes.", true)
 | 
												npmGithubPrComment("""Docker Image for build ${BUILD_NUMBER} is available on
 | 
				
			||||||
 | 
					[DockerHub](https://cloud.docker.com/repository/docker/nginxproxymanager/${IMAGE}-dev)
 | 
				
			||||||
 | 
					as `nginxproxymanager/${IMAGE}-dev:${BRANCH_LOWER}`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					**Note:** ensure you backup your NPM instance before testing this image! Especially if there are database changes
 | 
				
			||||||
 | 
					**Note:** this is a different docker image namespace than the official image
 | 
				
			||||||
 | 
					""", true)
 | 
				
			||||||
						}
 | 
											}
 | 
				
			||||||
					}
 | 
										}
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -313,6 +313,7 @@ const internalCertificate = {
 | 
				
			|||||||
					.where('is_deleted', 0)
 | 
										.where('is_deleted', 0)
 | 
				
			||||||
					.andWhere('id', data.id)
 | 
										.andWhere('id', data.id)
 | 
				
			||||||
					.allowGraph('[owner]')
 | 
										.allowGraph('[owner]')
 | 
				
			||||||
 | 
										.allowGraph('[proxy_hosts]')
 | 
				
			||||||
					.first();
 | 
										.first();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				if (access_data.permission_visibility !== 'all') {
 | 
									if (access_data.permission_visibility !== 'all') {
 | 
				
			||||||
@@ -464,6 +465,7 @@ const internalCertificate = {
 | 
				
			|||||||
					.where('is_deleted', 0)
 | 
										.where('is_deleted', 0)
 | 
				
			||||||
					.groupBy('id')
 | 
										.groupBy('id')
 | 
				
			||||||
					.allowGraph('[owner]')
 | 
										.allowGraph('[owner]')
 | 
				
			||||||
 | 
										.allowGraph('[proxy_hosts]')
 | 
				
			||||||
					.orderBy('nice_name', 'ASC');
 | 
										.orderBy('nice_name', 'ASC');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				if (access_data.permission_visibility !== 'all') {
 | 
									if (access_data.permission_visibility !== 'all') {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,6 +5,8 @@ const authModel  = require('../models/auth');
 | 
				
			|||||||
const helpers    = require('../lib/helpers');
 | 
					const helpers    = require('../lib/helpers');
 | 
				
			||||||
const TokenModel = require('../models/token');
 | 
					const TokenModel = require('../models/token');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const ERROR_MESSAGE_INVALID_AUTH = 'Invalid email or password';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
module.exports = {
 | 
					module.exports = {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
@@ -69,15 +71,15 @@ module.exports = {
 | 
				
			|||||||
													};
 | 
																		};
 | 
				
			||||||
												});
 | 
																	});
 | 
				
			||||||
										} else {
 | 
															} else {
 | 
				
			||||||
											throw new error.AuthError('Invalid password');
 | 
																throw new error.AuthError(ERROR_MESSAGE_INVALID_AUTH);
 | 
				
			||||||
										}
 | 
															}
 | 
				
			||||||
									});
 | 
														});
 | 
				
			||||||
							} else {
 | 
												} else {
 | 
				
			||||||
								throw new error.AuthError('No password auth for user');
 | 
													throw new error.AuthError(ERROR_MESSAGE_INVALID_AUTH);
 | 
				
			||||||
							}
 | 
												}
 | 
				
			||||||
						});
 | 
											});
 | 
				
			||||||
				} else {
 | 
									} else {
 | 
				
			||||||
					throw new error.AuthError('No relevant user found');
 | 
										throw new error.AuthError(ERROR_MESSAGE_INVALID_AUTH);
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
			});
 | 
								});
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,7 +4,6 @@
 | 
				
			|||||||
const db      = require('../db');
 | 
					const db      = require('../db');
 | 
				
			||||||
const helpers = require('../lib/helpers');
 | 
					const helpers = require('../lib/helpers');
 | 
				
			||||||
const Model   = require('objection').Model;
 | 
					const Model   = require('objection').Model;
 | 
				
			||||||
const User    = require('./user');
 | 
					 | 
				
			||||||
const now     = require('./now_helper');
 | 
					const now     = require('./now_helper');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Model.knex(db);
 | 
					Model.knex(db);
 | 
				
			||||||
@@ -68,6 +67,8 @@ class Certificate extends Model {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	static get relationMappings () {
 | 
						static get relationMappings () {
 | 
				
			||||||
 | 
							const ProxyHost = require('./proxy_host');
 | 
				
			||||||
 | 
							const User      = require('./user');
 | 
				
			||||||
		return {
 | 
							return {
 | 
				
			||||||
			owner: {
 | 
								owner: {
 | 
				
			||||||
				relation:   Model.HasOneRelation,
 | 
									relation:   Model.HasOneRelation,
 | 
				
			||||||
@@ -79,6 +80,17 @@ class Certificate extends Model {
 | 
				
			|||||||
				modify: function (qb) {
 | 
									modify: function (qb) {
 | 
				
			||||||
					qb.where('user.is_deleted', 0);
 | 
										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);
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		};
 | 
							};
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -19,7 +19,9 @@
 | 
				
			|||||||
		"incoming_port": {
 | 
							"incoming_port": {
 | 
				
			||||||
			"type": "integer",
 | 
								"type": "integer",
 | 
				
			||||||
			"minimum": 1,
 | 
								"minimum": 1,
 | 
				
			||||||
			"maximum": 65535
 | 
								"maximum": 65535,
 | 
				
			||||||
 | 
								"if": {"properties": {"tcp_forwarding": {"const": true}}},
 | 
				
			||||||
 | 
								"then": {"not": {"oneOf": [{"const": 80}, {"const": 443}]}}
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		"forwarding_host": {
 | 
							"forwarding_host": {
 | 
				
			||||||
			"anyOf": [
 | 
								"anyOf": [
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -49,8 +49,7 @@
 | 
				
			|||||||
										"minLength": 1
 | 
															"minLength": 1
 | 
				
			||||||
									},
 | 
														},
 | 
				
			||||||
									"password": {
 | 
														"password": {
 | 
				
			||||||
										"type": "string",
 | 
															"type": "string"
 | 
				
			||||||
										"minLength": 1
 | 
					 | 
				
			||||||
									}
 | 
														}
 | 
				
			||||||
								}
 | 
													}
 | 
				
			||||||
							}
 | 
												}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,7 +4,7 @@
 | 
				
			|||||||
    auth_basic            "Authorization required";
 | 
					    auth_basic            "Authorization required";
 | 
				
			||||||
    auth_basic_user_file  /data/access/{{ access_list_id }};
 | 
					    auth_basic_user_file  /data/access/{{ access_list_id }};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    {% if access_list.pass_auth == 0 %}
 | 
					    {% if access_list.pass_auth == 0 or access_list.pass_auth == true %}
 | 
				
			||||||
    proxy_set_header Authorization "";
 | 
					    proxy_set_header Authorization "";
 | 
				
			||||||
    {% endif %}
 | 
					    {% endif %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -17,7 +17,7 @@
 | 
				
			|||||||
    deny all;
 | 
					    deny all;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # Access checks must...
 | 
					    # Access checks must...
 | 
				
			||||||
    {% if access_list.satisfy_any == 1 %}
 | 
					    {% if access_list.satisfy_any == 1 or access_list.satisfy_any == true %}
 | 
				
			||||||
    satisfy any;
 | 
					    satisfy any;
 | 
				
			||||||
    {% else %}
 | 
					    {% else %}
 | 
				
			||||||
    satisfy all;
 | 
					    satisfy all;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,11 +5,16 @@
 | 
				
			|||||||
  #listen [::]:80;
 | 
					  #listen [::]:80;
 | 
				
			||||||
{% endif %}
 | 
					{% endif %}
 | 
				
			||||||
{% if certificate -%}
 | 
					{% if certificate -%}
 | 
				
			||||||
  listen 443 ssl{% if http2_support == 1 or http2_support == true %} http2{% endif %};
 | 
					  listen 443 ssl;
 | 
				
			||||||
{% if ipv6 -%}
 | 
					{% if ipv6 -%}
 | 
				
			||||||
  listen [::]:443 ssl{% if http2_support == 1 or http2_support == true %} http2{% endif %};
 | 
					  listen [::]:443 ssl;
 | 
				
			||||||
{% else -%}
 | 
					{% else -%}
 | 
				
			||||||
  #listen [::]:443;
 | 
					  #listen [::]:443;
 | 
				
			||||||
{% endif %}
 | 
					{% endif %}
 | 
				
			||||||
{% endif %}
 | 
					{% endif %}
 | 
				
			||||||
  server_name {{ domain_names | join: " " }};
 | 
					  server_name {{ domain_names | join: " " }};
 | 
				
			||||||
 | 
					{% if http2_support == 1 or http2_support == true %}
 | 
				
			||||||
 | 
					  http2 on;
 | 
				
			||||||
 | 
					{% else -%}
 | 
				
			||||||
 | 
					  http2 off;
 | 
				
			||||||
 | 
					{% endif %}
 | 
				
			||||||
@@ -7,11 +7,7 @@
 | 
				
			|||||||
    proxy_set_header X-Forwarded-For    $remote_addr;
 | 
					    proxy_set_header X-Forwarded-For    $remote_addr;
 | 
				
			||||||
    proxy_set_header X-Real-IP		$remote_addr;
 | 
					    proxy_set_header X-Real-IP		$remote_addr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    set $proxy_forward_scheme {{ forward_scheme }};
 | 
					    proxy_pass       {{ forward_scheme }}://{{ forward_host }}:{{ forward_port }}{{ forward_path }};
 | 
				
			||||||
    set $proxy_server         "{{ forward_host }}";
 | 
					 | 
				
			||||||
    set $proxy_port           {{ forward_port }};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    proxy_pass       $proxy_forward_scheme://$proxy_server:$proxy_port{{ forward_path }};
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    {% include "_access.conf" %}
 | 
					    {% include "_access.conf" %}
 | 
				
			||||||
    {% include "_assets.conf" %}
 | 
					    {% include "_assets.conf" %}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -830,9 +830,9 @@ crc32-stream@^4.0.2:
 | 
				
			|||||||
    readable-stream "^3.4.0"
 | 
					    readable-stream "^3.4.0"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
cross-spawn@^7.0.2:
 | 
					cross-spawn@^7.0.2:
 | 
				
			||||||
  version "7.0.3"
 | 
					  version "7.0.6"
 | 
				
			||||||
  resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6"
 | 
					  resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f"
 | 
				
			||||||
  integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==
 | 
					  integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==
 | 
				
			||||||
  dependencies:
 | 
					  dependencies:
 | 
				
			||||||
    path-key "^3.1.0"
 | 
					    path-key "^3.1.0"
 | 
				
			||||||
    shebang-command "^2.0.0"
 | 
					    shebang-command "^2.0.0"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,4 +1,4 @@
 | 
				
			|||||||
location ~* ^.*\.(css|js|jpe?g|gif|png|webp|woff|eot|ttf|svg|ico|css\.map|js\.map)$ {
 | 
					location ~* ^.*\.(css|js|jpe?g|gif|png|webp|woff|woff2|eot|ttf|svg|ico|css\.map|js\.map)$ {
 | 
				
			||||||
	if_modified_since off;
 | 
						if_modified_since off;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	# use the public cache
 | 
						# use the public cache
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -50,7 +50,6 @@ networks:
 | 
				
			|||||||
Let's look at a Portainer example:
 | 
					Let's look at a Portainer example:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```yml
 | 
					```yml
 | 
				
			||||||
version: '3.8'
 | 
					 | 
				
			||||||
services:
 | 
					services:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  portainer:
 | 
					  portainer:
 | 
				
			||||||
@@ -92,8 +91,6 @@ 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.
 | 
					You can set any environment variable from a file by appending `__FILE` (double-underscore FILE) to the environmental variable name.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```yml
 | 
					```yml
 | 
				
			||||||
version: '3.8'
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
secrets:
 | 
					secrets:
 | 
				
			||||||
  # Secrets are single-line text files where the sole content is the secret
 | 
					  # 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"
 | 
					  # Paths in this example assume that secrets are kept in local folder called ".secrets"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -9,7 +9,6 @@ outline: deep
 | 
				
			|||||||
Create a `docker-compose.yml` file:
 | 
					Create a `docker-compose.yml` file:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```yml
 | 
					```yml
 | 
				
			||||||
version: '3.8'
 | 
					 | 
				
			||||||
services:
 | 
					services:
 | 
				
			||||||
  app:
 | 
					  app:
 | 
				
			||||||
    image: 'jc21/nginx-proxy-manager:latest'
 | 
					    image: 'jc21/nginx-proxy-manager:latest'
 | 
				
			||||||
@@ -55,7 +54,6 @@ are going to use.
 | 
				
			|||||||
Here is an example of what your `docker-compose.yml` will look like when using a MariaDB container:
 | 
					Here is an example of what your `docker-compose.yml` will look like when using a MariaDB container:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```yml
 | 
					```yml
 | 
				
			||||||
version: '3.8'
 | 
					 | 
				
			||||||
services:
 | 
					services:
 | 
				
			||||||
  app:
 | 
					  app:
 | 
				
			||||||
    image: 'jc21/nginx-proxy-manager:latest'
 | 
					    image: 'jc21/nginx-proxy-manager:latest'
 | 
				
			||||||
@@ -137,5 +135,13 @@ Email:    admin@example.com
 | 
				
			|||||||
Password: changeme
 | 
					Password: changeme
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Immediately after logging in with this default user you will be asked to modify your details and change your password.
 | 
					Immediately after logging in with this default user you will be asked to modify your details and change your password. You can change defaults with:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					    environment:
 | 
				
			||||||
 | 
					      INITIAL_ADMIN_EMAIL: my@example.com
 | 
				
			||||||
 | 
					      INITIAL_ADMIN_PASSWORD: mypassword1
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -50,7 +50,6 @@ module.exports = Mn.View.extend({
 | 
				
			|||||||
    onRender: function () {
 | 
					    onRender: function () {
 | 
				
			||||||
        let view = this;
 | 
					        let view = this;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (typeof view.stats.hosts === 'undefined') {
 | 
					 | 
				
			||||||
        Api.Reports.getHostStats()
 | 
					        Api.Reports.getHostStats()
 | 
				
			||||||
                .then(response => {
 | 
					                .then(response => {
 | 
				
			||||||
                    if (!view.isDestroyed()) {
 | 
					                    if (!view.isDestroyed()) {
 | 
				
			||||||
@@ -61,7 +60,6 @@ module.exports = Mn.View.extend({
 | 
				
			|||||||
                .catch(err => {
 | 
					                .catch(err => {
 | 
				
			||||||
                    console.log(err);
 | 
					                    console.log(err);
 | 
				
			||||||
                });
 | 
					                });
 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,6 +1,6 @@
 | 
				
			|||||||
<td class="text-center">
 | 
					<td class="text-center">
 | 
				
			||||||
    <div class="avatar d-block" style="background-image: url(<%- owner.avatar || '/images/default-avatar.jpg' %>)" title="Owned by <%- owner.name %>">
 | 
					    <div class="avatar d-block" style="background-image: url(<%- (owner && owner.avatar) || '/images/default-avatar.jpg' %>)" title="Owned by <%- (owner && owner.name) || 'a deleted user' %>">
 | 
				
			||||||
        <span class="avatar-status <%- owner.is_disabled ? 'bg-red' : 'bg-green' %>"></span>
 | 
					        <span class="avatar-status <%- owner && !owner.is_disabled ? 'bg-green' : 'bg-red' %>"></span>
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
</td>
 | 
					</td>
 | 
				
			||||||
<td>
 | 
					<td>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,6 +1,6 @@
 | 
				
			|||||||
<td class="text-center">
 | 
					<td class="text-center">
 | 
				
			||||||
    <div class="avatar d-block" style="background-image: url(<%- owner.avatar || '/images/default-avatar.jpg' %>)" title="Owned by <%- owner.name %>">
 | 
					    <div class="avatar d-block" style="background-image: url(<%- (owner && owner.avatar) || '/images/default-avatar.jpg' %>)" title="Owned by <%- (owner && owner.name) || 'a deleted user' %>">
 | 
				
			||||||
        <span class="avatar-status <%- owner.is_disabled ? 'bg-red' : 'bg-green' %>"></span>
 | 
					        <span class="avatar-status <%- owner && !owner.is_disabled ? 'bg-green' : 'bg-red' %>"></span>
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
</td>
 | 
					</td>
 | 
				
			||||||
<td>
 | 
					<td>
 | 
				
			||||||
@@ -33,6 +33,13 @@
 | 
				
			|||||||
<td class="<%- isExpired() ? 'text-danger' : '' %>">
 | 
					<td class="<%- isExpired() ? 'text-danger' : '' %>">
 | 
				
			||||||
    <%- formatDbDate(expires_on, 'Do MMMM YYYY, h:mm a') %>
 | 
					    <%- formatDbDate(expires_on, 'Do MMMM YYYY, h:mm a') %>
 | 
				
			||||||
</td>
 | 
					</td>
 | 
				
			||||||
 | 
					<td>
 | 
				
			||||||
 | 
					    <% if (proxy_hosts.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) { %>
 | 
					<% if (canManage) { %>
 | 
				
			||||||
<td class="text-right">
 | 
					<td class="text-right">
 | 
				
			||||||
    <div class="item-action dropdown">
 | 
					    <div class="item-action dropdown">
 | 
				
			||||||
@@ -48,6 +55,13 @@
 | 
				
			|||||||
                <div class="dropdown-divider"></div>
 | 
					                <div class="dropdown-divider"></div>
 | 
				
			||||||
            <% } %>
 | 
					            <% } %>
 | 
				
			||||||
            <a href="#" class="delete dropdown-item"><i class="dropdown-icon fe fe-trash-2"></i> <%- i18n('str', 'delete') %></a>
 | 
					            <a href="#" class="delete dropdown-item"><i class="dropdown-icon fe fe-trash-2"></i> <%- i18n('str', 'delete') %></a>
 | 
				
			||||||
 | 
					            <% if (proxy_hosts.length > 0) { %>
 | 
				
			||||||
 | 
					                <div class="dropdown-divider"></div>
 | 
				
			||||||
 | 
					                <span class="dropdown-header"><%- i18n('certificates', 'active-domain_names') %></span>
 | 
				
			||||||
 | 
					                <% proxy_hosts.forEach(function(host) { %>
 | 
				
			||||||
 | 
					                    <a href="https://<%- host %>" class="dropdown-item" target="_blank"><%- host %></a>
 | 
				
			||||||
 | 
					                <% }); %>
 | 
				
			||||||
 | 
					            <% } %>
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
</td>
 | 
					</td>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -44,12 +44,23 @@ module.exports = Mn.View.extend({
 | 
				
			|||||||
        },
 | 
					        },
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    templateContext: {
 | 
					    templateContext: function () {
 | 
				
			||||||
 | 
					        return {
 | 
				
			||||||
            canManage: App.Cache.User.canManage('certificates'),
 | 
					            canManage: App.Cache.User.canManage('certificates'),
 | 
				
			||||||
            isExpired: function () {
 | 
					            isExpired: function () {
 | 
				
			||||||
                return moment(this.expires_on).isBefore(moment());
 | 
					                return moment(this.expires_on).isBefore(moment());
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
        dns_providers: dns_providers
 | 
					            dns_providers: dns_providers,
 | 
				
			||||||
 | 
					            proxy_hosts: this.getProxyHosts() 
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    getProxyHosts: function () {
 | 
				
			||||||
 | 
					        const hosts = this.model.attributes.proxy_hosts || [];
 | 
				
			||||||
 | 
					        return hosts.reduce((acc, host) => {
 | 
				
			||||||
 | 
					            acc.push(...(host.domain_names || []));
 | 
				
			||||||
 | 
					            return acc;
 | 
				
			||||||
 | 
					        }, []);
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    initialize: function () {
 | 
					    initialize: function () {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,6 +3,7 @@
 | 
				
			|||||||
    <th><%- i18n('str', 'name') %></th>
 | 
					    <th><%- i18n('str', 'name') %></th>
 | 
				
			||||||
    <th><%- i18n('all-hosts', 'cert-provider') %></th>
 | 
					    <th><%- i18n('all-hosts', 'cert-provider') %></th>
 | 
				
			||||||
    <th><%- i18n('str', 'expires') %></th>
 | 
					    <th><%- i18n('str', 'expires') %></th>
 | 
				
			||||||
 | 
					    <th><%- i18n('str', 'status') %></th>
 | 
				
			||||||
    <% if (canManage) { %>
 | 
					    <% if (canManage) { %>
 | 
				
			||||||
    <th> </th>
 | 
					    <th> </th>
 | 
				
			||||||
    <% } %>
 | 
					    <% } %>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -74,7 +74,7 @@ module.exports = Mn.View.extend({
 | 
				
			|||||||
            e.preventDefault();
 | 
					            e.preventDefault();
 | 
				
			||||||
            let query = this.ui.query.val();
 | 
					            let query = this.ui.query.val();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            this.fetch(['owner'], query)
 | 
					            this.fetch(['owner','proxy_hosts'], query)
 | 
				
			||||||
                .then(response => this.showData(response))
 | 
					                .then(response => this.showData(response))
 | 
				
			||||||
                .catch(err => {
 | 
					                .catch(err => {
 | 
				
			||||||
                    this.showError(err);
 | 
					                    this.showError(err);
 | 
				
			||||||
@@ -89,7 +89,7 @@ module.exports = Mn.View.extend({
 | 
				
			|||||||
    onRender: function () {
 | 
					    onRender: function () {
 | 
				
			||||||
        let view = this;
 | 
					        let view = this;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        view.fetch(['owner'])
 | 
					        view.fetch(['owner','proxy_hosts'])
 | 
				
			||||||
            .then(response => {
 | 
					            .then(response => {
 | 
				
			||||||
                if (!view.isDestroyed()) {
 | 
					                if (!view.isDestroyed()) {
 | 
				
			||||||
                    if (response && response.length) {
 | 
					                    if (response && response.length) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,6 +1,6 @@
 | 
				
			|||||||
<td class="text-center">
 | 
					<td class="text-center">
 | 
				
			||||||
    <div class="avatar d-block" style="background-image: url(<%- owner.avatar || '/images/default-avatar.jpg' %>)" title="Owned by <%- owner.name %>">
 | 
					    <div class="avatar d-block" style="background-image: url(<%- (owner && owner.avatar) || '/images/default-avatar.jpg' %>)" title="Owned by <%- (owner && owner.name) || 'a deleted user' %>">
 | 
				
			||||||
        <span class="avatar-status <%- owner.is_disabled ? 'bg-red' : 'bg-green' %>"></span>
 | 
					        <span class="avatar-status <%- owner && !owner.is_disabled ? 'bg-green' : 'bg-red' %>"></span>
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
</td>
 | 
					</td>
 | 
				
			||||||
<td>
 | 
					<td>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,6 +1,6 @@
 | 
				
			|||||||
<td class="text-center">
 | 
					<td class="text-center">
 | 
				
			||||||
    <div class="avatar d-block" style="background-image: url(<%- owner.avatar || '/images/default-avatar.jpg' %>)" title="Owned by <%- owner.name %>">
 | 
					    <div class="avatar d-block" style="background-image: url(<%- (owner && owner.avatar) || '/images/default-avatar.jpg' %>)" title="Owned by <%- (owner && owner.name) || 'a deleted user' %>">
 | 
				
			||||||
        <span class="avatar-status <%- owner.is_disabled ? 'bg-red' : 'bg-green' %>"></span>
 | 
					        <span class="avatar-status <%- owner && !owner.is_disabled ? 'bg-green' : 'bg-red' %>"></span>
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
</td>
 | 
					</td>
 | 
				
			||||||
<td>
 | 
					<td>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,6 +1,6 @@
 | 
				
			|||||||
<td class="text-center">
 | 
					<td class="text-center">
 | 
				
			||||||
    <div class="avatar d-block" style="background-image: url(<%- owner.avatar || '/images/default-avatar.jpg' %>)" title="Owned by <%- owner.name %>">
 | 
					    <div class="avatar d-block" style="background-image: url(<%- (owner && owner.avatar) || '/images/default-avatar.jpg' %>)" title="Owned by <%- (owner && owner.name) || 'a deleted user' %>">
 | 
				
			||||||
        <span class="avatar-status <%- owner.is_disabled ? 'bg-red' : 'bg-green' %>"></span>
 | 
					        <span class="avatar-status <%- owner && !owner.is_disabled ? 'bg-green' : 'bg-red' %>"></span>
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
</td>
 | 
					</td>
 | 
				
			||||||
<td>
 | 
					<td>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,6 +1,6 @@
 | 
				
			|||||||
<td class="text-center">
 | 
					<td class="text-center">
 | 
				
			||||||
    <div class="avatar d-block" style="background-image: url(<%- owner.avatar || '/images/default-avatar.jpg' %>)" title="Owned by <%- owner.name %>">
 | 
					    <div class="avatar d-block" style="background-image: url(<%- (owner && owner.avatar) || '/images/default-avatar.jpg' %>)" title="Owned by <%- (owner && owner.name) || 'a deleted user' %>">
 | 
				
			||||||
        <span class="avatar-status <%- owner.is_disabled ? 'bg-red' : 'bg-green' %>"></span>
 | 
					        <span class="avatar-status <%- owner && !owner.is_disabled ? 'bg-green' : 'bg-red' %>"></span>
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
</td>
 | 
					</td>
 | 
				
			||||||
<td>
 | 
					<td>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,10 +1,10 @@
 | 
				
			|||||||
<div class="modal-content">
 | 
					<div class="modal-content">
 | 
				
			||||||
 | 
					    <form>
 | 
				
			||||||
        <div class="modal-header">
 | 
					        <div class="modal-header">
 | 
				
			||||||
            <h5 class="modal-title"><%- i18n('users', 'form-title', {id: id}) %></h5>
 | 
					            <h5 class="modal-title"><%- i18n('users', 'form-title', {id: id}) %></h5>
 | 
				
			||||||
            <button type="button" class="close cancel" aria-label="Close" data-dismiss="modal"> </button>
 | 
					            <button type="button" class="close cancel" aria-label="Close" data-dismiss="modal"> </button>
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
        <div class="modal-body">
 | 
					        <div class="modal-body">
 | 
				
			||||||
        <form>
 | 
					 | 
				
			||||||
            <div class="row">
 | 
					            <div class="row">
 | 
				
			||||||
                <div class="col-sm-6 col-md-6">
 | 
					                <div class="col-sm-6 col-md-6">
 | 
				
			||||||
                    <div class="form-group">
 | 
					                    <div class="form-group">
 | 
				
			||||||
@@ -49,10 +49,10 @@
 | 
				
			|||||||
                </div>
 | 
					                </div>
 | 
				
			||||||
                <% } %>
 | 
					                <% } %>
 | 
				
			||||||
            </div>
 | 
					            </div>
 | 
				
			||||||
        </form>
 | 
					 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
        <div class="modal-footer">
 | 
					        <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-secondary cancel" data-dismiss="modal"><%- i18n('str', 'cancel') %></button>
 | 
				
			||||||
        <button type="button" class="btn btn-teal save"><%- i18n('str', 'save') %></button>
 | 
					            <button type="submit" class="btn btn-teal save"><%- i18n('str', 'save') %></button>
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
 | 
					    </form>
 | 
				
			||||||
</div>
 | 
					</div>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -19,7 +19,7 @@ module.exports = Mn.View.extend({
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    events: {
 | 
					    events: {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        'click @ui.save': function (e) {
 | 
					        'submit @ui.form': function (e) {
 | 
				
			||||||
            e.preventDefault();
 | 
					            e.preventDefault();
 | 
				
			||||||
            this.ui.error.hide();
 | 
					            this.ui.error.hide();
 | 
				
			||||||
            let view = this;
 | 
					            let view = this;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -206,7 +206,10 @@
 | 
				
			|||||||
      "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.",
 | 
					      "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",
 | 
					      "download": "Download",
 | 
				
			||||||
      "renew-title": "Renew Let's Encrypt Certificate",
 | 
					      "renew-title": "Renew Let's Encrypt Certificate",
 | 
				
			||||||
      "search": "Search Certificate…"
 | 
					      "search": "Search Certificate…",
 | 
				
			||||||
 | 
					      "in-use"  : "In use",
 | 
				
			||||||
 | 
					      "inactive": "Inactive",
 | 
				
			||||||
 | 
					      "active-domain_names": "Active domain names"
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "access-lists": {
 | 
					    "access-lists": {
 | 
				
			||||||
      "title": "Access Lists",
 | 
					      "title": "Access Lists",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2648,9 +2648,9 @@ electron-to-chromium@^1.3.47:
 | 
				
			|||||||
  integrity sha512-67V62Z4CFOiAtox+o+tosGfVk0QX4DJgH609tjT8QymbJZVAI/jWnAthnr8c5hnRNziIRwkc9EMQYejiVz3/9Q==
 | 
					  integrity sha512-67V62Z4CFOiAtox+o+tosGfVk0QX4DJgH609tjT8QymbJZVAI/jWnAthnr8c5hnRNziIRwkc9EMQYejiVz3/9Q==
 | 
				
			||||||
 | 
					
 | 
				
			||||||
elliptic@^6.5.3, elliptic@^6.5.4:
 | 
					elliptic@^6.5.3, elliptic@^6.5.4:
 | 
				
			||||||
  version "6.5.7"
 | 
					  version "6.6.0"
 | 
				
			||||||
  resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.7.tgz#8ec4da2cb2939926a1b9a73619d768207e647c8b"
 | 
					  resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.6.0.tgz#5919ec723286c1edf28685aa89261d4761afa210"
 | 
				
			||||||
  integrity sha512-ESVCtTwiA+XhY3wyh24QqRGBoP3rEdDUl3EDUUo9tft074fi19IrdpH7hLCMMP3CIj7jb3W96rn8lt/BqIlt5Q==
 | 
					  integrity sha512-dpwoQcLc/2WLQvJvLRHKZ+f9FgOdjnq11rurqwekGQygGPsYSK29OMMD2WalatiqQ+XGFDglTNixpPfI+lpaAA==
 | 
				
			||||||
  dependencies:
 | 
					  dependencies:
 | 
				
			||||||
    bn.js "^4.11.9"
 | 
					    bn.js "^4.11.9"
 | 
				
			||||||
    brorand "^1.1.0"
 | 
					    brorand "^1.1.0"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -303,6 +303,14 @@
 | 
				
			|||||||
		"credentials": "dns_joker_username = <Dynamic DNS Authentication Username>\ndns_joker_password = <Dynamic DNS Authentication Password>\ndns_joker_domain = <Dynamic DNS Domain>",
 | 
							"credentials": "dns_joker_username = <Dynamic DNS Authentication Username>\ndns_joker_password = <Dynamic DNS Authentication Password>\ndns_joker_domain = <Dynamic DNS Domain>",
 | 
				
			||||||
		"full_plugin_name": "dns-joker"
 | 
							"full_plugin_name": "dns-joker"
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
 | 
						"leaseweb": {
 | 
				
			||||||
 | 
							"name": "LeaseWeb",
 | 
				
			||||||
 | 
							"package_name": "certbot-dns-leaseweb",
 | 
				
			||||||
 | 
							"version": "~=1.0.1",
 | 
				
			||||||
 | 
							"dependencies": "",
 | 
				
			||||||
 | 
							"credentials": "dns_leaseweb_api_token = 01234556789",
 | 
				
			||||||
 | 
							"full_plugin_name": "dns-leaseweb"
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
	"linode": {
 | 
						"linode": {
 | 
				
			||||||
		"name": "Linode",
 | 
							"name": "Linode",
 | 
				
			||||||
		"package_name": "certbot-dns-linode",
 | 
							"package_name": "certbot-dns-linode",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -132,9 +132,9 @@
 | 
				
			|||||||
  integrity sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ==
 | 
					  integrity sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ==
 | 
				
			||||||
 | 
					
 | 
				
			||||||
"@eslint/plugin-kit@^0.2.0":
 | 
					"@eslint/plugin-kit@^0.2.0":
 | 
				
			||||||
  version "0.2.0"
 | 
					  version "0.2.3"
 | 
				
			||||||
  resolved "https://registry.yarnpkg.com/@eslint/plugin-kit/-/plugin-kit-0.2.0.tgz#8712dccae365d24e9eeecb7b346f85e750ba343d"
 | 
					  resolved "https://registry.yarnpkg.com/@eslint/plugin-kit/-/plugin-kit-0.2.3.tgz#812980a6a41ecf3a8341719f92a6d1e784a2e0e8"
 | 
				
			||||||
  integrity sha512-vH9PiIMMwvhCx31Af3HiGzsVNULDbyVkHXwlemn/B0TFj/00ho3y55efXrUZTfQipxoHC5u4xq6zblww1zm1Ig==
 | 
					  integrity sha512-2b/g5hRmpbb1o4GnTZax9N9m0FXzz9OV42ZzI4rDDMDuHUqigAiQCEWChBWCY4ztAGVRjoWT19v0yMmc5/L5kA==
 | 
				
			||||||
  dependencies:
 | 
					  dependencies:
 | 
				
			||||||
    levn "^0.4.1"
 | 
					    levn "^0.4.1"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -628,9 +628,9 @@ core-util-is@1.0.2:
 | 
				
			|||||||
  integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=
 | 
					  integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=
 | 
				
			||||||
 | 
					
 | 
				
			||||||
cross-spawn@^7.0.0, cross-spawn@^7.0.2:
 | 
					cross-spawn@^7.0.0, cross-spawn@^7.0.2:
 | 
				
			||||||
  version "7.0.3"
 | 
					  version "7.0.6"
 | 
				
			||||||
  resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6"
 | 
					  resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f"
 | 
				
			||||||
  integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==
 | 
					  integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==
 | 
				
			||||||
  dependencies:
 | 
					  dependencies:
 | 
				
			||||||
    path-key "^3.1.0"
 | 
					    path-key "^3.1.0"
 | 
				
			||||||
    shebang-command "^2.0.0"
 | 
					    shebang-command "^2.0.0"
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user