From accd4d775351c8819a512e993571c3d14cafabc8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Szekeres=20Ba=CC=81lint?= Date: Sun, 6 Jan 2019 12:26:28 +0100 Subject: [PATCH 01/13] loader --- resources/scss/_main.scss | 44 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/resources/scss/_main.scss b/resources/scss/_main.scss index f112fa7..01ec9e7 100644 --- a/resources/scss/_main.scss +++ b/resources/scss/_main.scss @@ -1,3 +1,11 @@ +@keyframes sk-bounce { + 0%, 100% { + transform: scale(0.0); + } 50% { + transform: scale(1.0); + } +} + section.tabs { margin-bottom: 1rem; @@ -134,12 +142,48 @@ section.tabs { } } } +section.loader { + position: fixed; + z-index: 1; + bottom: 25%; + left: 50%; + transform: translate(-50%, -50%); + pointer-events: none; + + .spinner { + width: 50px; + height: 50px; + + position: relative; + margin: 100px auto; + + .double-bounce1, + .double-bounce2 { + width: 100%; + height: 100%; + border-radius: 50%; + background-color: #999; + opacity: 0.6; + position: absolute; + top: 0; + left: 0; + animation: sk-bounce 2.0s infinite ease-in-out; + } + + .double-bounce2 { + animation-delay: -1.0s; } } } main { flex: 1 1 auto; + opacity: 0; + transition: opacity 0.25s ease-in-out; + + &.active { + opacity: 1; + } .commands { margin-bottom: 1rem; From a3116b51024eaa3142582156386809ff29664b3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Szekeres=20Ba=CC=81lint?= Date: Sun, 6 Jan 2019 15:48:28 +0100 Subject: [PATCH 02/13] multiple site support, site tabs, common tabs, django --- public/assets/img/brands/django.svg | 1 + public/assets/img/brands/python.svg | 1 + public/assets/js/app.js | 1098 +++++++---- public/index.html | 1654 +++++++++-------- public/templates/commands.html | 49 +- public/templates/conf/nginx.conf.html | 17 +- .../conf/nginxconfig.io/general.conf.html | 63 +- .../conf/nginxconfig.io/php_fastcgi.conf.html | 4 +- .../sites-available/example.com.conf.html | 175 +- resources/scss/_ads.scss | 2 + resources/scss/_header.scss | 1 + resources/scss/_main.scss | 169 +- 12 files changed, 1974 insertions(+), 1260 deletions(-) create mode 100644 public/assets/img/brands/django.svg create mode 100644 public/assets/img/brands/python.svg diff --git a/public/assets/img/brands/django.svg b/public/assets/img/brands/django.svg new file mode 100644 index 0000000..d369b65 --- /dev/null +++ b/public/assets/img/brands/django.svg @@ -0,0 +1 @@ +Django icon \ No newline at end of file diff --git a/public/assets/img/brands/python.svg b/public/assets/img/brands/python.svg new file mode 100644 index 0000000..ebfb15a --- /dev/null +++ b/public/assets/img/brands/python.svg @@ -0,0 +1 @@ +Python icon diff --git a/public/assets/js/app.js b/public/assets/js/app.js index ab70c40..251a7e6 100644 --- a/public/assets/js/app.js +++ b/public/assets/js/app.js @@ -13,81 +13,98 @@ var DEFAULTS = { - ipv4: '*', - ipv6: '::', + sites: [{ + // SERVER + domain: '', + path: '', + document_root: '/public', + non_www: true, + cdn: false, + redirect: true, + ipv4: '*', + ipv6: '::', - domain: '', - path: '', - document_root: '/public', + // HTTPS + https: true, + http2: true, + force_https: true, + hsts: true, + hsts_subdomains: true, + hsts_preload: true, + cert_type: 'letsencrypt', + email: '', + ssl_certificate: '', + ssl_certificate_key: '', - https: true, - http2: true, + // PHP + php: true, + wordpress: false, + drupal: false, + magento: false, - redirect: true, - force_https: true, + // PYTHON + python: false, + django: false, - cert_type: 'letsencrypt', + // PROXY + proxy: false, + proxy_path: '/', + proxy_pass: 'http://127.0.0.1:3000', + + // ROUTING + root: true, + index: 'index.php', + fallback_html: false, + fallback_php: true, + fallback_php_path: '/api/', + php_legacy_routing: false, + + // LOGGING + access_log_domain: false, + error_log_domain: false, + }], + + // COMMON - HTTPS ssl_profile: 'modern', - hsts: true, - hsts_subdomains: true, - hsts_preload: true, - email: '', - ssl_certificate: '', - ssl_certificate_key: '', - resolver_cloudflare: true, resolver_google: true, resolver_opendns: true, - non_www: true, - cdn: false, - - index: 'index.php', - fallback_html: false, - fallback_php: true, - fallback_php_path: '/api/', - - php: true, - php_server: '/var/run/php/php7.2-fpm.sock', - php_server_backup: '', - php_legacy_routing: false, - wordpress: false, - drupal: false, - magento: false, - - python: false, - python_server: '/tmp/uwsgi.sock', - python_server_backup: '', - - file_structure: 'modularized', - symlink: true, - + // COMMON - SECURITY referrer_policy: 'no-referrer-when-downgrade', content_security_policy: 'default-src * data: \'unsafe-eval\' \'unsafe-inline\'', + server_tokens: false, + limit_req: false, - worker_processes: 'auto', - user: 'www-data', - pid: '/run/nginx.pid', + // COMMON - PHP + php_server: '/var/run/php/php7.2-fpm.sock', + php_server_backup: '', + // COMMON - PYTHON + python_server: '/tmp/uwsgi.sock', + + // COMMON - PERFORMANCE + gzip: true, + brotli: false, + expires_assets: '7d', + expires_media: '7d', + expires_svg: '7d', + expires_fonts: '7d', + + // COMMON - LOGGING access_log: '/var/log/nginx/access.log', error_log: '/var/log/nginx/error.log warn', - access_log_domain: false, - error_log_domain: false, + log_not_found: false, + // COMMON - NGINX + worker_processes: 'auto', + user: 'www-data', + pid: '/run/nginx.pid', client_max_body_size: 16, - gzip: true, - server_tokens: false, - log_not_found: false, - limit_req: false, - expires_assets: '7d', - expires_media: '7d', - expires_svg: '7d', - expires_fonts: '7d', - - proxy: false, - proxy_path: '/', - proxy_pass: 'http://127.0.0.1:3000', + // COMMON - TOOLS + file_structure: 'modularized', + symlink: true, }; @@ -150,15 +167,182 @@ + /////////////////////// + // PRIVATE FUNCTIONS // + /////////////////////// + function getSiteValue(site, key) { + if (site === undefined) { + site = $scope.site; + } + + return $scope.data.sites[site][key]; + } + + function calculateChanges() { + var siteTabs = [ + 'server', + 'https', + 'php', + 'python', + 'proxy', + 'routing', + 'logging', + ]; + + var commonTabs = [ + 'https', + 'security', + 'php', + 'python', + 'performance', + 'logging', + 'nginx', + 'tools', + ]; + + if ($scope.siteChanges[$scope.site] === undefined) { + $scope.siteChanges[$scope.site] = {}; + } + + for (var i in siteTabs) { + $scope.siteChanges[$scope.site][siteTabs[i]] = $window.document.querySelectorAll('section.tabs .tab-content.site-tab-content .tab-' + siteTabs[i] + ' .form-group:not(.disabled) .input-changed').length; + } + + for (var j in commonTabs) { + $scope.commonChanges[commonTabs[j]] = $window.document.querySelectorAll('section.tabs .tab-content.common-content .tab-' + commonTabs[j] + ' .form-group:not(.disabled) .input-changed').length; + } + } + + function setDataFromHash() { + var hashData = $location.search(); + + for (var key in hashData) { + // handle false + if (hashData[key] === 'false') { + hashData[key] = false; + } + + // handle true + if ((hashData[key] === 'true' || hashData[key] === '') && typeof $scope.data[key] === 'boolean') { + hashData[key] = true; + } + + // handle sites + var sitesMatch = /^(\d+)\.(.+)$/.exec(key); + if (sitesMatch) { + var site = parseInt(sitesMatch[1]); + var siteKey = sitesMatch[2]; + + while (site >= $scope.data.sites.length) { + $scope.addSite(); + } + + if ( + $scope.data.sites[site][siteKey] !== undefined && + typeof $scope.data.sites[site][siteKey] === typeof hashData[key] + ) { + $scope.isDirty = true; + $scope.data.sites[site][siteKey] = hashData[key]; + gtag('event', key, { + event_category: 'data_from_hash', + event_label: hashData[key], + }); + } + } else if ( + $scope.data[key] !== undefined && + typeof $scope.data[key] === typeof hashData[key] + ) { + $scope.isDirty = true; + $scope.data[key] = hashData[key]; + gtag('event', key, { + event_category: 'data_from_hash', + event_label: hashData[key], + }); + } + } + + $scope.site = 0; + } + + function updateHash() { + if (!$scope.dataInit) { + return; + } + + var changedData = {}; + for (var key in $scope.data) { + if (!angular.equals($scope.data[key], $scope.defaultData[key])) { + if (key === 'sites') { + for (var i in $scope.data[key]) { + for (var j in $scope.data[key][i]) { + if ( + j !== '$$hashKey' && + !angular.equals( + $scope.data[key][i][j], + $scope.defaultData[key][0][j] + ) + ) { + changedData[i + '.' + j] = $scope.data[key][i][j]; + } + } + } + } else { + changedData[key] = $scope.data[key]; + } + } + } + + if (Object.keys(changedData).length) { + $scope.isDirty = true; + $location.search(changedData).replace(); + } else { + $scope.isDirty = false; + $location.search({}); + } + } + + function initMasonry() { + masonry = new Masonry('main .grid', { + itemSelector: '.grid-item', + columnWidth: '.grid-sizer', + percentPosition: true, + initLayout: false, + stagger: 0, + transitionDuration: '0.6s', + }); + + masonry.once('layoutComplete', function() { + $scope.masonryInit = true; + }); + } + + function doMasonry() { + masonry.reloadItems(); + masonry.layout(); + + $timeout(function() { + masonry.layout(); + }, 600); + } + + + ///////////////////// // SCOPE VARIABLES // ///////////////////// $scope.defaultData = DEFAULTS; - $scope.dataInit = false; - $scope.isDirty = false; - $scope.tab = 'site'; - $scope.data = angular.copy($scope.defaultData); + $scope.dataInit = false; + $scope.data = angular.copy($scope.defaultData); + $scope.isDirty = false; + $scope.masonryInit = false; + + $scope.site = 0; + $scope.tab_site = 'server'; + $scope.tab_common = 'https'; + + $scope.siteChanges = {}; + $scope.commonChanges = {}; $scope.clipboardCopy = undefined; @@ -197,113 +381,120 @@ ///////////////////// // SCOPE FUNCTIONS // ///////////////////// - $scope.domain = function() { - return $scope.data.domain ? $scope.data.domain : 'example.com'; - }; + $scope.getDomains = function() { + var domains = []; - $scope.sslCertificate = function() { - if ($scope.isCertLetsEncrypt()) { - return '/etc/letsencrypt/live/' + $scope.domain() + '/fullchain.pem'; + for (var i in $scope.data.sites) { + domains.push( $scope.getDomain(i) ); } - if ($scope.data.ssl_certificate) { - return $scope.data.ssl_certificate; - } - - return '/etc/nginx/ssl/' + $scope.domain() + '.crt'; - }; - - $scope.sslCertificateKey = function() { - if ($scope.isCertLetsEncrypt()) { - return '/etc/letsencrypt/live/' + $scope.domain() + '/privkey.pem'; - } - - if ($scope.data.ssl_certificate_key) { - return $scope.data.ssl_certificate_key; - } - - return '/etc/nginx/ssl/' + $scope.domain() + '.key'; - }; - - $scope.accessLogDomainPath = function() { - return $scope.data.access_log.replace(/([^/]+)\.log$/, $scope.domain() + '.$1.log'); - }; - - $scope.errorLogDomainPath = function() { - return $scope.data.error_log.replace(/([^/]+)\.log (.+)$/, $scope.domain() + '.$1.log $2'); - }; - - $scope.refreshHighlighting = function() { - var sourceCodes = $window.document.querySelectorAll('main .file .code.source'); - - for (var i = 0; i < sourceCodes.length; i++) { - var sourceCode = sourceCodes[i]; - - $timeout(function(_sourceCode) { - _sourceCode.nextSibling.innerHTML = _sourceCode.innerHTML; - if (_sourceCode.nextSibling.children.length && _sourceCode.nextSibling.children[0].children.length) { - hljs.highlightBlock(_sourceCode.nextSibling.children[0].children[0]); - } - _sourceCode.setAttribute('hidden', ''); - - $scope.doMasonry(); - }, 0, true, sourceCode); - } + return domains; }; $scope.getUrl = function() { return $location.absUrl().replace(/#.*$/, ''); }; - $scope.setDataFromHash = function() { - var hashData = $location.search(); - - for (var key in hashData) { - // handle false - if (hashData[key] === 'false') { - hashData[key] = false; - } - - // handle true - if ((hashData[key] === 'true' || hashData[key] === '') && typeof $scope.data[key] === 'boolean') { - hashData[key] = true; - } - - if ($scope.data[key] !== undefined && typeof $scope.data[key] === typeof hashData[key]) { - $scope.isDirty = true; - $scope.data[key] = hashData[key]; - gtag('event', key, { - event_category: 'data_from_hash', - event_label: hashData[key], - }); - } - } + $scope.addSite = function() { + $scope.data.sites.push( angular.copy(DEFAULTS.sites[0]) ); + $scope.site = $scope.data.sites.length - 1; }; - $scope.updateHash = function() { - if (!$scope.dataInit) { - return; + $scope.setSite = function(site) { + $scope.site = site; + $timeout(calculateChanges); + }; + + $scope.setTabSite = function(tab) { + $scope.tab_site = tab; + }; + + $scope.setTabCommon = function(tab) { + $scope.tab_common = tab; + }; + + $scope.setPreset = function(preset) { + $scope.data.sites[$scope.site].php = $scope.defaultData.sites[0].php; + $scope.data.sites[$scope.site].wordpress = $scope.defaultData.sites[0].wordpress; + $scope.data.sites[$scope.site].drupal = $scope.defaultData.sites[0].drupal; + $scope.data.sites[$scope.site].magento = $scope.defaultData.sites[0].magento; + $scope.data.sites[$scope.site].python = $scope.defaultData.sites[0].python; + $scope.data.sites[$scope.site].django = $scope.defaultData.sites[0].django; + $scope.data.sites[$scope.site].proxy = $scope.defaultData.sites[0].proxy; + $scope.data.sites[$scope.site].root = $scope.defaultData.sites[0].root; + $scope.data.sites[$scope.site].index = $scope.defaultData.sites[0].index; + $scope.data.sites[$scope.site].fallback_html = $scope.defaultData.sites[0].fallback_html; + + switch (preset) { + case 'frontend': + $scope.data.sites[$scope.site].php = false; + $scope.data.sites[$scope.site].index = 'index.html'; + $scope.data.sites[$scope.site].fallback_html = true; + break; + case 'backend': + $scope.data.sites[$scope.site].index = 'index.php'; + break; + case 'spa': + $scope.data.sites[$scope.site].index = 'index.html'; + $scope.data.sites[$scope.site].fallback_html = true; + break; + case 'wordpress': + $scope.data.sites[$scope.site].wordpress = true; + break; + case 'drupal': + $scope.data.sites[$scope.site].drupal = true; + break; + case 'magento': + $scope.data.sites[$scope.site].magento = true; + break; + case 'nodejs': + $scope.data.sites[$scope.site].proxy = true; + break; + case 'django': + $scope.data.sites[$scope.site].php = false; + $scope.data.sites[$scope.site].python = true; + $scope.data.sites[$scope.site].django = true; + $scope.data.sites[$scope.site].root = false; + break; } - var changedData = {}; - for (var key in $scope.data) { - if (!angular.equals($scope.data[key], $scope.defaultData[key])) { - changedData[key] = $scope.data[key]; - } + gtag('event', preset, { + event_category: 'preset', + }); + }; + + $scope.getSiteChanges = function(site) { + if ($scope.siteChanges[site] === undefined) { + return undefined; } - if (Object.keys(changedData).length) { - $scope.isDirty = true; - $location.search(changedData).replace(); - } else { - $scope.isDirty = false; - $location.search({}); + var sum = 0; + + for (var tab in $scope.siteChanges[site]) { + sum += $scope.siteChanges[site][tab]; } + + return sum; + }; + + $scope.getSiteTabChanges = function(tab) { + if ($scope.siteChanges[$scope.site] === undefined) { + return 0; + } + + return $scope.siteChanges[$scope.site][tab]; }; $scope.reset = function() { $scope.defaultData.index = 'index.php'; - $scope.data = angular.copy($scope.defaultData); + + $scope.data = angular.copy($scope.defaultData); + $scope.site = 0; + $scope.isDirty = false; + + $scope.siteChanges = {}; + $scope.commonChanges = {}; + gtag('event', 'reset'); }; @@ -335,10 +526,10 @@ type: 'blob', platform: 'UNIX', }).then(function(content) { - saveAs(content, 'nginxconfig.io-' + $scope.domain() + '.zip'); + saveAs(content, 'nginxconfig.io-' + $scope.getDomains().join(',') + '.zip'); }); - gtag('event', $scope.domain(), { + gtag('event', $scope.getDomains().join(','), { event_category: 'download_zip', }); }; @@ -357,74 +548,22 @@ }); }; - $scope.doMasonry = function() { - masonry.reloadItems(); - masonry.layout(); + $scope.refreshHighlighting = function() { + var sourceCodes = $window.document.querySelectorAll('main .file .code.source'); - $timeout(function() { - masonry.layout(); - }, 600); - }; + for (var i = 0; i < sourceCodes.length; i++) { + var sourceCode = sourceCodes[i]; - $scope.initMasonry = function() { - masonry = new Masonry('main .grid', { - itemSelector: '.grid-item', - columnWidth: '.grid-sizer', - percentPosition: true, - initLayout: false, - stagger: 0, - transitionDuration: '0.6s', - }); - }; + $timeout(function(_sourceCode) { + _sourceCode.nextSibling.innerHTML = _sourceCode.innerHTML; + if (_sourceCode.nextSibling.children.length && _sourceCode.nextSibling.children[0].children.length) { + hljs.highlightBlock(_sourceCode.nextSibling.children[0].children[0]); + } + _sourceCode.setAttribute('hidden', ''); - $scope.setTab = function(tab) { - $scope.tab = tab; - }; - - $scope.setPreset = function(preset) { - $scope.data.php = $scope.defaultData.php; - $scope.data.wordpress = $scope.defaultData.wordpress; - $scope.data.drupal = $scope.defaultData.drupal; - $scope.data.magento = $scope.defaultData.magento; - $scope.data.python = $scope.defaultData.python; - $scope.data.proxy = $scope.defaultData.proxy; - $scope.data.index = $scope.defaultData.index; - $scope.data.fallback_html = $scope.defaultData.fallback_html; - - switch (preset) { - case 'frontend': - $scope.data.php = false; - $scope.data.index = 'index.html'; - $scope.data.fallback_html = true; - break; - case 'backend': - $scope.data.index = 'index.php'; - break; - case 'spa': - $scope.data.index = 'index.html'; - $scope.data.fallback_html = true; - break; - case 'wordpress': - $scope.data.wordpress = true; - break; - case 'drupal': - $scope.data.drupal = true; - break; - case 'magento': - $scope.data.magento = true; - break; - case 'nodejs': - $scope.data.proxy = true; - break; + doMasonry(); + }, 0, true, sourceCode); } - - gtag('event', preset, { - event_category: 'preset', - }); - }; - - $scope.getChangesForTab = function(tab) { - return $window.document.querySelectorAll('section.tabs .tab-content .tab-' + tab + ' .input-changed').length; }; @@ -432,142 +571,368 @@ /////////////////////////// // SCOPE QUERY FUNCTIONS // /////////////////////////// - $scope.isUnified = function() { - return $scope.data.file_structure === 'unified'; + + // SERVER + $scope.getDomain = function(site) { + return getSiteValue(site, 'domain') || 'example.com'; }; - $scope.isIPv6 = function() { - return !!$scope.data.ipv6; + $scope.getPath = function(site) { + return getSiteValue(site, 'path') || '/var/www/' + $scope.getDomain(site); }; - $scope.isModularized = function() { - return !$scope.isUnified(); + $scope.isNonWWW = function(site) { + return getSiteValue(site, 'non_www'); }; - $scope.isHTTPS = function() { - return $scope.data.https; + $scope.isWWW = function(site) { + return !$scope.isNonWWW(site); }; - $scope.isHTTP2 = function() { - return $scope.isHTTPS() && $scope.data.http2; + $scope.isCDN = function(site) { + return $scope.isWWW(site) && getSiteValue(site, 'cdn'); }; - $scope.isForceHTTPS = function() { - return $scope.isHTTPS() && $scope.data.force_https; + $scope.isRedirect = function(site) { + return getSiteValue(site, 'redirect'); }; - $scope.isCertLetsEncrypt = function() { - return $scope.isHTTPS() && $scope.data.cert_type === 'letsencrypt'; + $scope.isIPv6 = function(site) { + return !!getSiteValue(site, 'ipv6'); }; - $scope.isCertCustom = function() { - return $scope.isHTTPS() && $scope.data.cert_type === 'custom'; + + + // HTTPS + $scope.isHTTPS = function(site) { + return getSiteValue(site, 'https'); }; + $scope.isHTTP2 = function(site) { + return $scope.isHTTPS(site) && getSiteValue(site, 'http2'); + }; + + $scope.isForceHTTPS = function(site) { + return $scope.isHTTPS(site) && getSiteValue(site, 'force_https'); + }; + + $scope.isHSTS = function(site) { + return $scope.isHTTPS(site) && getSiteValue(site, 'hsts'); + }; + + $scope.isHSTSSubdomains = function(site) { + return $scope.isHSTS(site) && getSiteValue(site, 'hsts_subdomains'); + }; + + $scope.isHSTSPreload = function(site) { + return $scope.isHSTSSubdomains(site) && getSiteValue(site, 'hsts_preload'); + }; + + $scope.isCertLetsEncrypt = function(site) { + return $scope.isHTTPS(site) && getSiteValue(site, 'cert_type') === 'letsencrypt'; + }; + + $scope.isCertCustom = function(site) { + return $scope.isHTTPS(site) && getSiteValue(site, 'cert_type') === 'custom'; + }; + + $scope.hasHTTPS = function() { + for (var site in $scope.data.sites) { + if ($scope.isHTTPS(site)) { + return true; + } + } + + return false; + }; + + $scope.hasCertLetsEncrypt = function() { + for (var site in $scope.data.sites) { + if ($scope.isCertLetsEncrypt(site)) { + return true; + } + } + + return false; + }; + + $scope.hasCertCustom = function() { + for (var site in $scope.data.sites) { + if ($scope.isCertCustom(site)) { + return true; + } + } + + return false; + }; + + $scope.getSslCertificate = function(site) { + if ($scope.isCertLetsEncrypt(site)) { + return '/etc/letsencrypt/live/' + $scope.getDomain(site) + '/fullchain.pem'; + } + + return getSiteValue(site, 'ssl_certificate') || '/etc/nginx/ssl/' + $scope.getDomain(site) + '.crt'; + }; + + $scope.getSslCertificateKey = function(site) { + if ($scope.isCertLetsEncrypt(site)) { + return '/etc/letsencrypt/live/' + $scope.getDomain(site) + '/privkey.pem'; + } + + return getSiteValue(site, 'ssl_certificate_key') || '/etc/nginx/ssl/' + $scope.getDomain(site) + '.key'; + }; + + $scope.hasCommonHSTS = function() { + var commonHSTSSubdomains = undefined; + var commonHSTSPreload = undefined; + + for (var site in $scope.data.sites) { + if (!$scope.isHSTS(site)) { + return false; + } + + if (commonHSTSSubdomains === undefined ) { + commonHSTSSubdomains = $scope.isHSTSSubdomains(site); + } else if ($scope.isHSTSSubdomains(site) !== commonHSTSSubdomains) { + return false; + } + + if (commonHSTSPreload === undefined ) { + commonHSTSPreload = $scope.isHSTSPreload(site); + } else if ($scope.isHSTSPreload(site) !== commonHSTSPreload) { + return false; + } + } + + return true; + }; + + + + // PHP + $scope.isPHP = function(site) { + return getSiteValue(site, 'php'); + }; + + $scope.isWordPress = function(site) { + return $scope.isPHP(site) && getSiteValue(site, 'wordpress'); + }; + + $scope.isDrupal = function(site) { + return $scope.isPHP(site) && getSiteValue(site, 'drupal'); + }; + + $scope.isMagento = function(site) { + return $scope.isPHP(site) && getSiteValue(site, 'magento'); + }; + + $scope.hasPHP = function() { + for (var site in $scope.data.sites) { + if ($scope.isPHP(site)) { + return true; + } + } + + return false; + }; + + $scope.hasWordPress = function() { + for (var site in $scope.data.sites) { + if ($scope.isWordPress(site)) { + return true; + } + } + + return false; + }; + + $scope.hasDrupal = function() { + for (var site in $scope.data.sites) { + if ($scope.isDrupal(site)) { + return true; + } + } + + return false; + }; + + $scope.hasMagento = function() { + for (var site in $scope.data.sites) { + if ($scope.isMagento(site)) { + return true; + } + } + + return false; + }; + + $scope.isLegacyPHPRouting = function(site) { + return $scope.isPHP(site) && getSiteValue(site, 'php_legacy_routing'); + }; + + $scope.hasLegacyPHPRouting = function() { + for (var site in $scope.data.sites) { + if ($scope.isLegacyPHPRouting(site)) { + return true; + } + } + + return false; + }; + + + + // PYTHON + $scope.isPython = function(site) { + return getSiteValue(site, 'python'); + }; + + $scope.isDjango = function(site) { + return $scope.isPython(site) && getSiteValue(site, 'django'); + }; + + $scope.hasPython = function() { + for (var site in $scope.data.sites) { + if ($scope.isPython(site)) { + return true; + } + } + + return false; + }; + + $scope.hasDjango = function() { + for (var site in $scope.data.sites) { + if ($scope.isDjango(site)) { + return true; + } + } + + return false; + }; + + + + // PROXY + $scope.isProxy = function(site) { + return getSiteValue(site, 'proxy'); + }; + + + + // ROUTING + $scope.isRoot = function(site) { + return getSiteValue(site, 'root'); + }; + + $scope.allRoot = function() { + for (var site in $scope.data.sites) { + if (!$scope.isRoot(site)) { + return false; + } + } + + return true; + }; + + $scope.isIndexHTML = function(site) { + return getSiteValue(site, 'index') === 'index.html' || !$scope.isPHP(site); + }; + + $scope.isIndexPHP = function(site) { + return $scope.isPHP(site) && getSiteValue(site, 'index') === 'index.php'; + }; + + $scope.isFallbackHTML = function(site) { + return getSiteValue(site, 'fallback_html'); + }; + + $scope.isFallbackPHP = function(site) { + return getSiteValue(site, 'fallback_php') && $scope.isPHP(site); + }; + + + + // LOGGING + $scope.isAccessLogDomain = function(site) { + return getSiteValue(site, 'access_log_domain'); + }; + + $scope.isErrorLogDomain = function(site) { + return getSiteValue(site, 'error_log_domain'); + }; + + $scope.getAccessLogDomainPath = function(site) { + return $scope.data.access_log.replace(/([^/]+)\.log$/, $scope.getDomain(site) + '.$1.log'); + }; + + $scope.getErrorLogDomainPath = function(site) { + return $scope.data.error_log.replace(/([^/]+)\.log (.+)$/, $scope.getDomain(site) + '.$1.log $2'); + }; + + + + // COMMON - HTTPS $scope.isSSLProfileModern = function() { - return $scope.isHTTPS() && $scope.data.ssl_profile === 'modern'; + return $scope.hasHTTPS() && $scope.data.ssl_profile === 'modern'; }; $scope.isSSLProfileIntermediate = function() { - return $scope.isHTTPS() && $scope.data.ssl_profile === 'intermediate'; + return $scope.hasHTTPS() && $scope.data.ssl_profile === 'intermediate'; }; $scope.isSSLProfileOld = function() { - return $scope.isHTTPS() && $scope.data.ssl_profile === 'old'; - }; - - $scope.isHSTS = function() { - return $scope.isHTTPS() && $scope.data.hsts; - }; - - $scope.isHSTSSubdomains = function() { - return $scope.isHSTS() && $scope.data.hsts_subdomains; - }; - - $scope.isHSTSPreload = function() { - return $scope.isHSTSSubdomains() && $scope.data.hsts_preload; + return $scope.hasHTTPS() && $scope.data.ssl_profile === 'old'; }; $scope.isResolverCloudflare = function() { - return $scope.isHTTPS() && $scope.data.resolver_cloudflare; + return $scope.hasHTTPS() && $scope.data.resolver_cloudflare; }; $scope.isResolverGoogle = function() { - return $scope.isHTTPS() && $scope.data.resolver_google; + return $scope.hasHTTPS() && $scope.data.resolver_google; }; $scope.isResolverOpenDNS = function() { - return $scope.isHTTPS() && $scope.data.resolver_opendns; + return $scope.hasHTTPS() && $scope.data.resolver_opendns; }; - $scope.isNonWWW = function() { - return $scope.data.non_www; - }; - $scope.isWWW = function() { - return !$scope.isNonWWW(); - }; - - $scope.isRedirect = function() { - return $scope.data.redirect; - }; - - $scope.isCDN = function() { - return $scope.isWWW() && $scope.data.cdn; - }; - - $scope.isIndexHTML = function() { - return $scope.data.index === 'index.html' || !$scope.isPHP(); - }; - - $scope.isIndexPHP = function() { - return $scope.isPHP() && $scope.data.index === 'index.php'; - }; - - $scope.isFallbackHTML = function() { - return $scope.data.fallback_html; - }; - - $scope.isFallbackPHP = function() { - return $scope.data.fallback_php && $scope.isPHP(); - }; - - $scope.isLegacyPHPRouting = function() { - return $scope.isPHP() && $scope.data.php_legacy_routing; - }; - - $scope.isPHP = function() { - return $scope.data.php; - }; - - $scope.isPHPBackup = function() { - return $scope.isPHP() && !!$scope.data.php_server_backup; - }; - - $scope.isWordPress = function() { - return $scope.isPHP() && $scope.data.wordpress; - }; - - $scope.isDrupal= function() { - return $scope.isPHP() && $scope.data.drupal; - }; - - $scope.isMagento = function() { - return $scope.isPHP() && $scope.data.magento; - }; - - $scope.isPython = function() { - return $scope.data.python; - }; - - $scope.isPythonBackup = function() { - return $scope.isPython() && !!$scope.data.python_server_backup; - }; + // COMMON - SECURITY $scope.isCSP = function() { return !!$scope.data.content_security_policy; }; + $scope.isServerTokens = function() { + return $scope.data.server_tokens; + }; + + $scope.isLimitReq = function() { + return $scope.data.limit_req; + }; + + + + // COMMON - PHP + $scope.isPHPBackup = function() { + return $scope.hasPHP() && !!$scope.data.php_server_backup; + }; + + + + // COMMON - PERFORMANCE + $scope.isGzip = function() { + return $scope.data.gzip; + }; + + $scope.isBrotli = function() { + return $scope.data.brotli; + }; + + + + // COMMON - LOGGING $scope.isAccessLog = function() { return !!$scope.data.access_log; }; @@ -576,32 +941,19 @@ return !!$scope.data.error_log; }; - $scope.isAccessLogDomain = function() { - return $scope.data.access_log_domain; - }; - - $scope.isErrorLogDomain = function() { - return $scope.data.error_log_domain; - }; - - $scope.isGzip = function() { - return $scope.data.gzip; - }; - - $scope.isServerTokens = function() { - return $scope.data.server_tokens; - }; - $scope.isLogNotFound = function() { return $scope.data.log_not_found; }; - $scope.isLimitReq = function() { - return $scope.data.limit_req; + + + // COMMON - TOOLS + $scope.isUnified = function() { + return $scope.data.file_structure === 'unified'; }; - $scope.isProxy = function() { - return $scope.data.proxy; + $scope.isModularized = function() { + return !$scope.isUnified(); }; $scope.isSymlink = function() { @@ -614,41 +966,59 @@ // SCOPE EVENTS // ////////////////// $scope.$watch('data', function(newValue, oldValue) { - $scope.refreshHighlighting(); - $scope.updateHash(); + $timeout($scope.refreshHighlighting); - // toggle PHP <-> Python - if ($scope.data.php && !oldValue.php) { - $scope.data.python = false; - } else if ($scope.data.python && !oldValue.python) { - $scope.data.php = false; + for (var site in $scope.data.sites) { + // www + $scope.data.sites[site].domain = $scope.data.sites[site].domain.replace(/^https?:\/\//, ''); + $scope.data.sites[site].domain = $scope.data.sites[site].domain.replace(/\/.*$/, ''); + + if ($scope.data.sites[site].domain.match(/^www\./)) { + $scope.data.sites[site].domain = $scope.data.sites[site].domain.replace(/^www./, ''); + $scope.data.sites[site].non_www = false; + } + + // toggle PHP <-> Python + if ( + $scope.data.sites[site].php && + oldValue.sites[site] && + !oldValue.sites[site].php + ) { + $scope.data.sites[site].python = false; + } else if ( + $scope.data.sites[site].python && + oldValue.sites[site] && + !oldValue.sites[site].python + ) { + $scope.data.sites[site].php = false; + } + + // toggle proxy <-> (PHP || Python) + if ( + $scope.data.sites[site].proxy && + oldValue.sites[site] && + !oldValue.sites[site].proxy + ) { + $scope.data.sites[site].php = false; + $scope.data.sites[site].python = false; + } else if ( + ( + $scope.data.sites[site].php && + oldValue.sites[site] && + !oldValue.sites[site].php + ) || + ( + $scope.data.sites[site].python && + oldValue.sites[site] && + !oldValue.sites[site].python + ) + ) { + $scope.data.sites[site].proxy = false; + } } - // toggle proxy <-> (PHP || Python) - if ($scope.data.proxy && !oldValue.proxy) { - $scope.data.php = false; - $scope.data.python = false; - } else if ( - ($scope.data.php && !oldValue.php) || - ($scope.data.python && !oldValue.python) - ) { - $scope.data.proxy = false; - } - - // default index file - if (!$scope.data.php) { - $scope.defaultData.index = 'index.html'; - } else { - $scope.defaultData.index = 'index.php'; - } - - $scope.data.domain = $scope.data.domain.replace(/^https?:\/\//, ''); - $scope.data.domain = $scope.data.domain.replace(/\/.*$/, ''); - - if ($scope.data.domain.match(/^www\./)) { - $scope.data.domain = $scope.data.domain.replace(/^www./, ''); - $scope.data.non_www = false; - } + updateHash(); + $timeout(calculateChanges); for (var key in $scope.data) { if (!angular.equals(newValue[key], oldValue[key])) { @@ -669,7 +1039,7 @@ ////////// // INIT // ////////// - $scope.setDataFromHash(); - $scope.initMasonry(); + setDataFromHash(); + initMasonry(); } })(); diff --git a/public/index.html b/public/index.html index d8102c8..c425319 100644 --- a/public/index.html +++ b/public/index.html @@ -21,13 +21,78 @@
- - - - - - - + + + + + + + +
@@ -35,768 +100,848 @@
- -
-
-
- -
- -
-
-
- -
- -
-
-
- -
- -
-
-
- -
-
- - -
-
-
-
- -
-
- - -
-
-
-
- -
-
- - -
-
-
-
- -
-
-
-
IPv4
+
+ +
+
+ +
+
+
+
+
+ + +
+
+
+
+ + +
+
+
+
+ + +
+
+
+ +
+
+ + +
+
+
+
+ +
+
+ + +
+
+
+
+ +
+
+ + +
+
+
+
+ +
+
+
+
IPv4
+
+ +
+
+
+
+
+
IPv6
+
+ +
+
+
+
+
+
+ +
+
+ + +
+
+
+
+ +
+
+ + +
+
+
+
+ +
+
+ + +
+
+
+
+ +
+
+ + +
+
+ + +
+
+ + +
+
+
+
+
+ Certification type +
+
+ + +
+
+ + +
+
+
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+
+
+ +
+
+ + +
+
+
+
+ +
+
+ + +
+
+
+
+ +
+
+ + +
+
+
+
+ +
+
+ + +
+
+
+
+
+
+ +
+
+ + +
+
+
+
+ +
+
+ + +
+
+
+
+
+
+ +
+
+ + +
+
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+
+
+ +
+
+ + +
+
+
+
+
+ + index + +
+
+ + +
+
+ + +
+
+
+
+
+
+ + Fallback routing + +
+
+ + +
+
+ + +
+
+
+
+
+ +
+ +
+
+
+ +
+
+ + +
+
+
+
+
+
+ +
+
+ + +
+
+
+
+ +
+
+ + +
+
+
+
+
+
+
+
+ +
+ +
+
+
+
+ SSL profile +
+
+ + +
+
+ + +
+
+ + +
+
+
+
+
+ +
+
+ + +
+
+ + +
+
+ + +
+
+
+
+
+
+ +
+ +
+
+
+ +
+ ng-model="data.content_security_policy" + ng-class="{ 'input-changed': data.content_security_policy !== defaultData.content_security_policy }">
-
-
-
-
IPv6
+
+ +
+
+ +
+
+
+
+ +
+
+ + +
+
+
+
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+
+
+ +
+ ng-model="data.python_server" + ng-class="{ 'input-changed': data.python_server !== defaultData.python_server }">
-
-
-
- -
-
- - -
-
-
-
- -
-
- - -
-
-
-
- -
-
- - -
-
-
-
- -
-
- - -
-
- - -
-
- - -
-
-
-
-
- SSL profile +
+
+
-
- - -
-
- - -
-
- - +
+ +
-
-
-
- Certification type +
+
-
- - -
-
- - +
+ +
-
-
- -
- -
-
-
- -
- -
-
-
- -
- -
-
-
- -
-
- - -
-
- - -
-
- - -
-
-
-
-
-
- -
- -
-
-
- -
- -
-
-
- -
-
- - -
-
-
-
- -
-
- - -
-
-
-
-
-
- -
-
- - -
-
-
-
- -
- -
-
-
- -
- -
-
-
- -
-
- - -
-
-
-
- -
-
- - -
-
-
-
- -
-
- - -
-
-
-
-
-
- -
-
- - -
-
-
-
- -
- -
-
-
- -
- -
-
-
-
-
-
- - index - +
+
-
- - -
-
- - -
-
-
-
-
-
- - Fallback routing - -
-
- - -
-
- - -
-
-
-
-
- -
- -
-
-
- -
-
- - -
-
-
-
-
-
- -
- -
-
-
- -
- -
-
-
- -
- -
-
-
- -
- -
-
-
-
-
- -
-
- - -
-
-
-
- -
- -
-
-
- -
- -
-
-
-
-
- -
- -
-
-
- -
- -
-
-
- -
-
- - -
-
-
-
- -
-
- - -
-
-
-
- -
-
- - -
-
-
-
-
-
- -
- -
-
-
- -
- -
-
-
- -
- -
-
-
- -
-
- -
-
MB
+ ng-model="data.expires_assets" + ng-class="{ 'input-changed': data.expires_assets !== defaultData.expires_assets }"> +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+
+ +
-
-
-
- -
- +
+
+ +
+ +
-
-
- -
-
- - +
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+
+ +
+
MB
+
+
-
- -
-
- - +
+
+ +
+
+ + +
-
-
- -
- +
+ +
+
+ + +
+
-
-
- -
- +
+ +
+ +
+
+
+ +
+ +
@@ -825,7 +970,13 @@
-
+
+
+
+
+
+
+
@@ -834,6 +985,9 @@
+
+ +
/etc/nginx/nginx.conf
-
- /etc/nginx/sites-{{ isSymlink() ? 'available' : 'enabled' }}/{{ domain() }}.conf - Copied! -
+
-
+
-
+
/etc/nginx/nginxconfig.io/letsencrypt.conf
-
+
/etc/nginx/nginxconfig.io/php_fastcgi.conf
-
+
/etc/nginx/nginxconfig.io/python_uwsgi.conf
-
+
/etc/nginx/nginxconfig.io/proxy.conf
-
+
/etc/nginx/nginxconfig.io/wordpress.conf
-
+
/etc/nginx/nginxconfig.io/drupal.conf
-
+
/etc/nginx/nginxconfig.io/magento.conf +
-
+
index @@ -493,7 +493,7 @@
-
+
Fallback routing From f3503830eddb961a1e8ce248afd2501ad25a615e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Szekeres=20Ba=CC=81lint?= Date: Sun, 6 Jan 2019 17:23:42 +0100 Subject: [PATCH 12/13] general.conf whitespace fixes --- .../conf/nginxconfig.io/general.conf.html | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/public/templates/conf/nginxconfig.io/general.conf.html b/public/templates/conf/nginxconfig.io/general.conf.html index d69b641..f492950 100644 --- a/public/templates/conf/nginxconfig.io/general.conf.html +++ b/public/templates/conf/nginxconfig.io/general.conf.html @@ -15,7 +15,7 @@ location ~ /\.(?!well-known) { deny all; } +✘ root --> access_log off; -} +✔ gzip --> -# gzip +# gzip gzip on; gzip_vary on; gzip_proxied any; @@ -80,7 +78,9 @@ gzip_types {{ gzipTypes }};# brotli +✔ brotli --> + +# brotli brotli on; brotli_comp_level 6; brotli_types {{ gzipTypes }}; From 0198f5a31e3f83a665fe83e3929932d6c7b1cda2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Szekeres=20Ba=CC=81lint?= Date: Sun, 6 Jan 2019 17:30:48 +0100 Subject: [PATCH 13/13] general.conf whitespace fixes --- public/templates/conf/nginxconfig.io/general.conf.html | 4 ---- 1 file changed, 4 deletions(-) diff --git a/public/templates/conf/nginxconfig.io/general.conf.html b/public/templates/conf/nginxconfig.io/general.conf.html index f492950..9b47925 100644 --- a/public/templates/conf/nginxconfig.io/general.conf.html +++ b/public/templates/conf/nginxconfig.io/general.conf.html @@ -74,10 +74,6 @@ gzip_proxied any; gzip_comp_level 6; gzip_types {{ gzipTypes }}; - - # brotli