mirror of
				https://github.com/NginxProxyManager/nginx-proxy-manager.git
				synced 2025-11-04 09:25:15 +00:00 
			
		
		
		
	Merge commit 'c5aa2b9f771cbd4c78c239ed0791aeb8d9e4d2e4' into features/dns-cloudflare
This commit is contained in:
		@@ -141,16 +141,11 @@ const internalCertificate = {
 | 
				
			|||||||
								});
 | 
													});
 | 
				
			||||||
						})
 | 
											})
 | 
				
			||||||
						.then((in_use_result) => {
 | 
											.then((in_use_result) => {
 | 
				
			||||||
							// 3. Generate the LE config
 | 
												// Is CloudFlare, no config needed, so skip 3 and 5.
 | 
				
			||||||
							return internalNginx.generateLetsEncryptRequestConfig(certificate)
 | 
												if (data.meta.cloudflare_use) {
 | 
				
			||||||
								.then(internalNginx.reload)
 | 
													return internalNginx.reload().then(() => {
 | 
				
			||||||
								.then(() => {
 | 
					 | 
				
			||||||
									// 4. Request cert
 | 
														// 4. Request cert
 | 
				
			||||||
									return internalCertificate.requestLetsEncryptSsl(certificate);
 | 
														return internalCertificate.requestLetsEncryptCloudFlareDnsSsl(certificate, data.meta.cloudflare_token);
 | 
				
			||||||
								})
 | 
					 | 
				
			||||||
								.then(() => {
 | 
					 | 
				
			||||||
									// 5. Remove LE config
 | 
					 | 
				
			||||||
									return internalNginx.deleteLetsEncryptRequestConfig(certificate);
 | 
					 | 
				
			||||||
								})
 | 
													})
 | 
				
			||||||
								.then(internalNginx.reload)
 | 
													.then(internalNginx.reload)
 | 
				
			||||||
								.then(() => {
 | 
													.then(() => {
 | 
				
			||||||
@@ -162,15 +157,44 @@ const internalCertificate = {
 | 
				
			|||||||
								})
 | 
													})
 | 
				
			||||||
								.catch((err) => {
 | 
													.catch((err) => {
 | 
				
			||||||
									// In the event of failure, revert things and throw err back
 | 
														// In the event of failure, revert things and throw err back
 | 
				
			||||||
									return internalNginx.deleteLetsEncryptRequestConfig(certificate)
 | 
														return internalCertificate.enableInUseHosts(in_use_result)
 | 
				
			||||||
										.then(() => {
 | 
					 | 
				
			||||||
											return internalCertificate.enableInUseHosts(in_use_result);
 | 
					 | 
				
			||||||
										})
 | 
					 | 
				
			||||||
										.then(internalNginx.reload)
 | 
															.then(internalNginx.reload)
 | 
				
			||||||
										.then(() => {
 | 
															.then(() => {
 | 
				
			||||||
											throw err;
 | 
																throw err;
 | 
				
			||||||
										});
 | 
															});
 | 
				
			||||||
								});
 | 
													});
 | 
				
			||||||
 | 
												} else {
 | 
				
			||||||
 | 
													// 3. Generate the LE config
 | 
				
			||||||
 | 
													return internalNginx.generateLetsEncryptRequestConfig(certificate)
 | 
				
			||||||
 | 
														.then(internalNginx.reload)
 | 
				
			||||||
 | 
														.then(() => {
 | 
				
			||||||
 | 
															// 4. Request cert
 | 
				
			||||||
 | 
															return internalCertificate.requestLetsEncryptSsl(certificate);
 | 
				
			||||||
 | 
														})
 | 
				
			||||||
 | 
														.then(() => {
 | 
				
			||||||
 | 
															// 5. Remove LE config
 | 
				
			||||||
 | 
															return internalNginx.deleteLetsEncryptRequestConfig(certificate);
 | 
				
			||||||
 | 
														})
 | 
				
			||||||
 | 
														.then(internalNginx.reload)
 | 
				
			||||||
 | 
														.then(() => {
 | 
				
			||||||
 | 
															// 6. Re-instate previously disabled hosts
 | 
				
			||||||
 | 
															return internalCertificate.enableInUseHosts(in_use_result);
 | 
				
			||||||
 | 
														})
 | 
				
			||||||
 | 
														.then(() => {
 | 
				
			||||||
 | 
															return certificate;
 | 
				
			||||||
 | 
														})
 | 
				
			||||||
 | 
														.catch((err) => {
 | 
				
			||||||
 | 
															// In the event of failure, revert things and throw err back
 | 
				
			||||||
 | 
															return internalNginx.deleteLetsEncryptRequestConfig(certificate)
 | 
				
			||||||
 | 
																.then(() => {
 | 
				
			||||||
 | 
																	return internalCertificate.enableInUseHosts(in_use_result);
 | 
				
			||||||
 | 
																})
 | 
				
			||||||
 | 
																.then(internalNginx.reload)
 | 
				
			||||||
 | 
																.then(() => {
 | 
				
			||||||
 | 
																	throw err;
 | 
				
			||||||
 | 
																});
 | 
				
			||||||
 | 
														});
 | 
				
			||||||
 | 
												}
 | 
				
			||||||
						})
 | 
											})
 | 
				
			||||||
						.then(() => {
 | 
											.then(() => {
 | 
				
			||||||
							// At this point, the letsencrypt cert should exist on disk.
 | 
												// At this point, the letsencrypt cert should exist on disk.
 | 
				
			||||||
@@ -748,6 +772,39 @@ const internalCertificate = {
 | 
				
			|||||||
			});
 | 
								});
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * @param   {Object}  certificate   the certificate row
 | 
				
			||||||
 | 
						 * @param	{String} apiToken		the cloudflare api token
 | 
				
			||||||
 | 
						 * @returns {Promise}
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						requestLetsEncryptCloudFlareDnsSsl: (certificate, apiToken) => {
 | 
				
			||||||
 | 
							logger.info('Requesting Let\'sEncrypt certificates via Cloudflare DNS for Cert #' + certificate.id + ': ' + certificate.domain_names.join(', '));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							let tokenLoc = '~/cloudflare-token';
 | 
				
			||||||
 | 
							let storeKey = 'echo "dns_cloudflare_api_token = ' + apiToken + '" > ' + tokenLoc;	
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							let cmd = 
 | 
				
			||||||
 | 
								storeKey + " && " +
 | 
				
			||||||
 | 
								certbot_command + ' certonly --non-interactive ' +
 | 
				
			||||||
 | 
								'--cert-name "npm-' + certificate.id + '" ' +
 | 
				
			||||||
 | 
								'--agree-tos ' +
 | 
				
			||||||
 | 
								'--email "' + certificate.meta.letsencrypt_email + '" ' +			
 | 
				
			||||||
 | 
								'--domains "' + certificate.domain_names.join(',') + '" ' +
 | 
				
			||||||
 | 
								'--dns-cloudflare --dns-cloudflare-credentials ' + tokenLoc +
 | 
				
			||||||
 | 
								(le_staging ? ' --staging' : '')
 | 
				
			||||||
 | 
								+ ' && rm ' + tokenLoc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (debug_mode) {
 | 
				
			||||||
 | 
								logger.info('Command:', cmd);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							return utils.exec(cmd).then((result) => {
 | 
				
			||||||
 | 
									logger.info(result);
 | 
				
			||||||
 | 
									return result;
 | 
				
			||||||
 | 
								});
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
	 * @param   {Access}  access
 | 
						 * @param   {Access}  access
 | 
				
			||||||
	 * @param   {Object}  data
 | 
						 * @param   {Object}  data
 | 
				
			||||||
@@ -761,7 +818,9 @@ const internalCertificate = {
 | 
				
			|||||||
			})
 | 
								})
 | 
				
			||||||
			.then((certificate) => {
 | 
								.then((certificate) => {
 | 
				
			||||||
				if (certificate.provider === 'letsencrypt') {
 | 
									if (certificate.provider === 'letsencrypt') {
 | 
				
			||||||
					return internalCertificate.renewLetsEncryptSsl(certificate)
 | 
										let renewMethod = certificate.meta.cloudflare_use ? internalCertificate.renewLetsEncryptCloudFlareSsl : internalCertificate.renewLetsEncryptSsl;		
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										return renewMethod(certificate)
 | 
				
			||||||
						.then(() => {
 | 
											.then(() => {
 | 
				
			||||||
							return internalCertificate.getCertificateInfoFromFile('/etc/letsencrypt/live/npm-' + certificate.id + '/fullchain.pem');
 | 
												return internalCertificate.getCertificateInfoFromFile('/etc/letsencrypt/live/npm-' + certificate.id + '/fullchain.pem');
 | 
				
			||||||
						})
 | 
											})
 | 
				
			||||||
@@ -815,6 +874,29 @@ const internalCertificate = {
 | 
				
			|||||||
			});
 | 
								});
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * @param   {Object}  certificate   the certificate row
 | 
				
			||||||
 | 
						 * @returns {Promise}
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						renewLetsEncryptCloudFlareSsl: (certificate) => {
 | 
				
			||||||
 | 
							logger.info('Renewing Let\'sEncrypt certificates for Cert #' + certificate.id + ': ' + certificate.domain_names.join(', '));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							let cmd = certbot_command + ' renew --non-interactive ' +
 | 
				
			||||||
 | 
								'--cert-name "npm-' + certificate.id + '" ' +
 | 
				
			||||||
 | 
								'--disable-hook-validation ' +
 | 
				
			||||||
 | 
								(le_staging ? '--staging' : '');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (debug_mode) {
 | 
				
			||||||
 | 
								logger.info('Command:', cmd);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							return utils.exec(cmd)
 | 
				
			||||||
 | 
								.then((result) => {
 | 
				
			||||||
 | 
									logger.info(result);
 | 
				
			||||||
 | 
									return result;
 | 
				
			||||||
 | 
								});
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
	 * @param   {Object}  certificate    the certificate row
 | 
						 * @param   {Object}  certificate    the certificate row
 | 
				
			||||||
	 * @param   {Boolean} [throw_errors]
 | 
						 * @param   {Boolean} [throw_errors]
 | 
				
			||||||
@@ -824,7 +906,6 @@ const internalCertificate = {
 | 
				
			|||||||
		logger.info('Revoking Let\'sEncrypt certificates for Cert #' + certificate.id + ': ' + certificate.domain_names.join(', '));
 | 
							logger.info('Revoking Let\'sEncrypt certificates for Cert #' + certificate.id + ': ' + certificate.domain_names.join(', '));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		let cmd = certbot_command + ' revoke --non-interactive ' +
 | 
							let cmd = certbot_command + ' revoke --non-interactive ' +
 | 
				
			||||||
			'--config "' + le_config + '" ' +
 | 
					 | 
				
			||||||
			'--cert-path "/etc/letsencrypt/live/npm-' + certificate.id + '/fullchain.pem" ' +
 | 
								'--cert-path "/etc/letsencrypt/live/npm-' + certificate.id + '/fullchain.pem" ' +
 | 
				
			||||||
			'--delete-after-revoke ' +
 | 
								'--delete-after-revoke ' +
 | 
				
			||||||
			(le_staging ? '--staging' : '');
 | 
								(le_staging ? '--staging' : '');
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -41,6 +41,12 @@
 | 
				
			|||||||
        },
 | 
					        },
 | 
				
			||||||
        "letsencrypt_agree": {
 | 
					        "letsencrypt_agree": {
 | 
				
			||||||
          "type": "boolean"
 | 
					          "type": "boolean"
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        "cloudflare_use": {
 | 
				
			||||||
 | 
					          "type": "boolean"
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        "cloudflare_token": {
 | 
				
			||||||
 | 
					          "type": "string"
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,7 +7,8 @@ ENV S6_FIX_ATTRS_HIDDEN=1
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
RUN echo "fs.file-max = 65535" > /etc/sysctl.conf \
 | 
					RUN echo "fs.file-max = 65535" > /etc/sysctl.conf \
 | 
				
			||||||
	&& apk update \
 | 
						&& apk update \
 | 
				
			||||||
	&& apk add python2 certbot jq \
 | 
						&& apk add python2 py-pip certbot jq \
 | 
				
			||||||
 | 
						&& pip install certbot-dns-cloudflare \
 | 
				
			||||||
	&& rm -rf /var/cache/apk/*
 | 
						&& rm -rf /var/cache/apk/*
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Task
 | 
					# Task
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -20,6 +20,24 @@
 | 
				
			|||||||
                            <input name="meta[letsencrypt_email]" type="email" class="form-control" placeholder="" value="<%- getLetsencryptEmail() %>" required>
 | 
					                            <input name="meta[letsencrypt_email]" type="email" class="form-control" placeholder="" value="<%- getLetsencryptEmail() %>" required>
 | 
				
			||||||
                        </div>
 | 
					                        </div>
 | 
				
			||||||
                    </div>
 | 
					                    </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    <!-- CloudFlare -->
 | 
				
			||||||
 | 
					                    <div class="col-sm-12 col-md-12">
 | 
				
			||||||
 | 
					                        <div class="form-group">
 | 
				
			||||||
 | 
					                            <label class="custom-switch">
 | 
				
			||||||
 | 
					                                <input type="checkbox" class="custom-switch-input" name="meta[cloudflare_use]" value="1">
 | 
				
			||||||
 | 
					                                <span class="custom-switch-indicator"></span>
 | 
				
			||||||
 | 
					                                <span class="custom-switch-description"><%= i18n('ssl', 'use-cloudflare') %></span>
 | 
				
			||||||
 | 
					                            </label>
 | 
				
			||||||
 | 
					                        </div>
 | 
				
			||||||
 | 
					                    </div>
 | 
				
			||||||
 | 
					                    <div class="col-sm-12 col-md-12 cloudflare">
 | 
				
			||||||
 | 
					                        <div class="form-group">
 | 
				
			||||||
 | 
					                            <label class="form-label">CloudFlare DNS API Token  <span class="form-required">*</span></label>
 | 
				
			||||||
 | 
					                            <input type="text" name="meta[cloudflare_token]" class="form-control" id="cloudflare_token">
 | 
				
			||||||
 | 
					                        </div>
 | 
				
			||||||
 | 
					                    </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    <div class="col-sm-12 col-md-12">
 | 
					                    <div class="col-sm-12 col-md-12">
 | 
				
			||||||
                        <div class="form-group">
 | 
					                        <div class="form-group">
 | 
				
			||||||
                            <label class="custom-switch">
 | 
					                            <label class="custom-switch">
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -20,15 +20,29 @@ module.exports = Mn.View.extend({
 | 
				
			|||||||
        save:                           'button.save',
 | 
					        save:                           'button.save',
 | 
				
			||||||
        other_certificate:              '#other_certificate',
 | 
					        other_certificate:              '#other_certificate',
 | 
				
			||||||
        other_certificate_key:          '#other_certificate_key',
 | 
					        other_certificate_key:          '#other_certificate_key',
 | 
				
			||||||
        other_intermediate_certificate: '#other_intermediate_certificate'
 | 
					        other_intermediate_certificate: '#other_intermediate_certificate',
 | 
				
			||||||
 | 
					        cloudflare_switch:              'input[name="meta[cloudflare_use]"]',
 | 
				
			||||||
 | 
					        cloudflare_token:               'input[name="meta[cloudflare_token]"',
 | 
				
			||||||
 | 
					        cloudflare:                     '.cloudflare'
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    events: {
 | 
					    events: {
 | 
				
			||||||
 | 
					        'change @ui.cloudflare_switch': function() {
 | 
				
			||||||
 | 
					            let checked = this.ui.cloudflare_switch.prop('checked');
 | 
				
			||||||
 | 
					            if (checked) {                
 | 
				
			||||||
 | 
					                this.ui.cloudflare_token.prop('required', 'required');
 | 
				
			||||||
 | 
					                this.ui.cloudflare.show();
 | 
				
			||||||
 | 
					            } else {                
 | 
				
			||||||
 | 
					                this.ui.cloudflare_token.prop('required', false);
 | 
				
			||||||
 | 
					                this.ui.cloudflare.hide();                
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
        'click @ui.save': function (e) {
 | 
					        'click @ui.save': function (e) {
 | 
				
			||||||
            e.preventDefault();
 | 
					            e.preventDefault();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (!this.ui.form[0].checkValidity()) {
 | 
					            if (!this.ui.form[0].checkValidity()) {
 | 
				
			||||||
                $('<input type="submit">').hide().appendTo(this.ui.form).click().remove();
 | 
					                $('<input type="submit">').hide().appendTo(this.ui.form).click().remove();
 | 
				
			||||||
 | 
					                $(this).removeClass('btn-loading');
 | 
				
			||||||
                return;
 | 
					                return;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -36,10 +50,29 @@ module.exports = Mn.View.extend({
 | 
				
			|||||||
            let data      = this.ui.form.serializeJSON();
 | 
					            let data      = this.ui.form.serializeJSON();
 | 
				
			||||||
            data.provider = this.model.get('provider');
 | 
					            data.provider = this.model.get('provider');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            let domain_err = false;
 | 
				
			||||||
 | 
					            if (!data.meta.cloudflare_use) {                
 | 
				
			||||||
 | 
					                data.domain_names.split(',').map(function (name) {
 | 
				
			||||||
 | 
					                    if (name.match(/\*/im)) {
 | 
				
			||||||
 | 
					                        domain_err = true;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (domain_err) {
 | 
				
			||||||
 | 
					                alert('Cannot request Let\'s Encrypt Certificate for wildcard domains when not using CloudFlare DNS');
 | 
				
			||||||
 | 
					                return;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // Manipulate
 | 
					            // Manipulate
 | 
				
			||||||
            if (typeof data.meta !== 'undefined' && typeof data.meta.letsencrypt_agree !== 'undefined') {
 | 
					            if (typeof data.meta !== 'undefined' && typeof data.meta.letsencrypt_agree !== 'undefined') {
 | 
				
			||||||
                data.meta.letsencrypt_agree = !!data.meta.letsencrypt_agree;
 | 
					                data.meta.letsencrypt_agree = !!data.meta.letsencrypt_agree;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					            if (typeof data.meta !== 'undefined' && typeof data.meta.cloudflare_use !== 'undefined') {
 | 
				
			||||||
 | 
					                data.meta.cloudflare_use = !!data.meta.cloudflare_use;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (typeof data.domain_names === 'string' && data.domain_names) {
 | 
					            if (typeof data.domain_names === 'string' && data.domain_names) {
 | 
				
			||||||
                data.domain_names = data.domain_names.split(',');
 | 
					                data.domain_names = data.domain_names.split(',');
 | 
				
			||||||
@@ -81,6 +114,7 @@ module.exports = Mn.View.extend({
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            this.ui.buttons.prop('disabled', true).addClass('btn-disabled');
 | 
					            this.ui.buttons.prop('disabled', true).addClass('btn-disabled');
 | 
				
			||||||
 | 
					            this.ui.save.addClass('btn-loading');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // compile file data
 | 
					            // compile file data
 | 
				
			||||||
            let form_data = new FormData();
 | 
					            let form_data = new FormData();
 | 
				
			||||||
@@ -119,6 +153,7 @@ module.exports = Mn.View.extend({
 | 
				
			|||||||
                .catch(err => {
 | 
					                .catch(err => {
 | 
				
			||||||
                    alert(err.message);
 | 
					                    alert(err.message);
 | 
				
			||||||
                    this.ui.buttons.prop('disabled', false).removeClass('btn-disabled');
 | 
					                    this.ui.buttons.prop('disabled', false).removeClass('btn-disabled');
 | 
				
			||||||
 | 
					                    this.ui.save.removeClass('btn-loading');
 | 
				
			||||||
                });
 | 
					                });
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
@@ -130,6 +165,10 @@ module.exports = Mn.View.extend({
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        getLetsencryptAgree: function () {
 | 
					        getLetsencryptAgree: function () {
 | 
				
			||||||
            return typeof this.meta.letsencrypt_agree !== 'undefined' ? this.meta.letsencrypt_agree : false;
 | 
					            return typeof this.meta.letsencrypt_agree !== 'undefined' ? this.meta.letsencrypt_agree : false;
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        getCloudflareUse: function () {
 | 
				
			||||||
 | 
					            return typeof this.meta.cloudflare_use !== 'undefined' ? this.meta.cloudflare_use : false;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -144,8 +183,9 @@ module.exports = Mn.View.extend({
 | 
				
			|||||||
                    text:  input
 | 
					                    text:  input
 | 
				
			||||||
                };
 | 
					                };
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
            createFilter: /^(?:[^.*]+\.?)+[^.]$/
 | 
					            createFilter: /^(?:[^.]+\.?)+[^.]$/
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
 | 
					        this.ui.cloudflare.hide();
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    initialize: function (options) {
 | 
					    initialize: function (options) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -101,7 +101,8 @@
 | 
				
			|||||||
      "letsencrypt-email": "Email Address for Let's Encrypt",
 | 
					      "letsencrypt-email": "Email Address for Let's Encrypt",
 | 
				
			||||||
      "letsencrypt-agree": "I Agree to the <a href=\"{url}\" target=\"_blank\">Let's Encrypt Terms of Service</a>",
 | 
					      "letsencrypt-agree": "I Agree to the <a href=\"{url}\" target=\"_blank\">Let's Encrypt Terms of Service</a>",
 | 
				
			||||||
      "delete-ssl": "The SSL certificates attached will NOT be removed, they will need to be removed manually.",
 | 
					      "delete-ssl": "The SSL certificates attached will NOT be removed, they will need to be removed manually.",
 | 
				
			||||||
      "hosts-warning": "These domains must be already configured to point to this installation"
 | 
					      "hosts-warning": "These domains must be already configured to point to this installation",
 | 
				
			||||||
 | 
					      "use-cloudflare": "Use CloudFlare DNS verification"      
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "proxy-hosts": {
 | 
					    "proxy-hosts": {
 | 
				
			||||||
      "title": "Proxy Hosts",
 | 
					      "title": "Proxy Hosts",
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user