Global config base & https tab
This commit is contained in:
parent
71622272b5
commit
5d3110d349
|
@ -1,5 +1,5 @@
|
||||||
export default {
|
export default {
|
||||||
title: 'NGINXConfig',
|
title: 'NGINXConfig',
|
||||||
description: '',
|
description: 'The easiest way to configure a performant, secure, and stable NGINX server.',
|
||||||
oss: 'This tool is {link|open-source on GitHub|https://github.com/do-community/nginxconfig-vue} under the {link|Apache-2.0|https://github.com/do-community/nginxconfig-vue/blob/master/LICENSE} license! We welcome feedback and contributions.',
|
oss: 'This tool is {link|open-source on GitHub|https://github.com/do-community/nginxconfig-vue} under the {link|Apache-2.0|https://github.com/do-community/nginxconfig-vue/blob/master/LICENSE} license! We welcome feedback and contributions.',
|
||||||
};
|
};
|
||||||
|
|
|
@ -27,6 +27,8 @@ limitations under the License.
|
||||||
</Header>
|
</Header>
|
||||||
|
|
||||||
<div class="main container">
|
<div class="main container">
|
||||||
|
<h2>Per-website config</h2>
|
||||||
|
|
||||||
<div class="tabs">
|
<div class="tabs">
|
||||||
<ul>
|
<ul>
|
||||||
<li v-for="data in activeDomains" :class="data[1] === active ? 'is-active' : undefined">
|
<li v-for="data in activeDomains" :class="data[1] === active ? 'is-active' : undefined">
|
||||||
|
@ -47,6 +49,9 @@ limitations under the License.
|
||||||
:style="{ display: data[1] === active ? 'block' : 'none' }"
|
:style="{ display: data[1] === active ? 'block' : 'none' }"
|
||||||
></Domain>
|
></Domain>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<h2>Global config</h2>
|
||||||
|
<Global :data="global"></Global>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Footer :text="i18n.templates.app.oss"></Footer>
|
<Footer :text="i18n.templates.app.oss"></Footer>
|
||||||
|
@ -60,13 +65,15 @@ limitations under the License.
|
||||||
import isChanged from '../util/is_changed';
|
import isChanged from '../util/is_changed';
|
||||||
import i18n from '../i18n';
|
import i18n from '../i18n';
|
||||||
import Domain from './domain';
|
import Domain from './domain';
|
||||||
|
import Global from './global';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'App',
|
name: 'App',
|
||||||
components: {
|
components: {
|
||||||
Domain,
|
|
||||||
Header,
|
Header,
|
||||||
Footer,
|
Footer,
|
||||||
|
Domain,
|
||||||
|
Global,
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
@ -74,6 +81,7 @@ limitations under the License.
|
||||||
domains: [
|
domains: [
|
||||||
clone(Domain.delegated),
|
clone(Domain.delegated),
|
||||||
],
|
],
|
||||||
|
global: Global.delegated,
|
||||||
active: 0,
|
active: 0,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
|
@ -0,0 +1,108 @@
|
||||||
|
<!--
|
||||||
|
Copyright 2020 DigitalOcean
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="panel">
|
||||||
|
<div class="tabs">
|
||||||
|
<ul>
|
||||||
|
<li v-for="tab in tabs" :class="tabClass(tab.key)">
|
||||||
|
<a @click="active = tab.key">{{ tab.display }}{{ changes(tab.key) }}</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<component :is="tab"
|
||||||
|
v-for="tab in tabs"
|
||||||
|
:key="tab.key"
|
||||||
|
:data="$props.data[tab.key]"
|
||||||
|
:style="{ display: active === tab.key ? 'block' : 'none' }"
|
||||||
|
class="container"
|
||||||
|
></component>
|
||||||
|
|
||||||
|
<div class="navigation-buttons">
|
||||||
|
<a v-if="previousTab !== false" class="button is-mini" @click="active = previousTab">
|
||||||
|
<i class="fas fa-long-arrow-alt-left"></i> <span>Back</span>
|
||||||
|
</a>
|
||||||
|
<a v-if="nextTab !== false" class="button is-primary is-mini" @click="active = nextTab">
|
||||||
|
<span>Next</span> <i class="fas fa-long-arrow-alt-right"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import isChanged from '../util/is_changed';
|
||||||
|
import * as Sections from './global_sections';
|
||||||
|
|
||||||
|
const tabs = Object.values(Sections);
|
||||||
|
const delegated = tabs.reduce((prev, tab) => {
|
||||||
|
prev[tab.key] = tab.delegated;
|
||||||
|
return prev;
|
||||||
|
}, {});
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'Global',
|
||||||
|
delegated, // Data the parent will present here
|
||||||
|
props: {
|
||||||
|
data: Object, // Data delegated back to us from parent
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
active: tabs[0].key,
|
||||||
|
tabs,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
nextTab() {
|
||||||
|
const tabs = this.$data.tabs.map(t => t.key);
|
||||||
|
const index = tabs.indexOf(this.$data.active) + 1;
|
||||||
|
if (index < tabs.length) return tabs[index];
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
previousTab() {
|
||||||
|
const tabs = this.$data.tabs.map(t => t.key);
|
||||||
|
const index = tabs.indexOf(this.$data.active) - 1;
|
||||||
|
if (index >= 0) return tabs[index];
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
changesCount(tab) {
|
||||||
|
return Object.keys(this.$props.data[tab])
|
||||||
|
.filter(key => isChanged(this.$props.data[tab][key], tab, key)).length;
|
||||||
|
},
|
||||||
|
changes(tab) {
|
||||||
|
const changes = this.changesCount(tab);
|
||||||
|
if (changes) return ` (${changes.toLocaleString()})`;
|
||||||
|
return '';
|
||||||
|
},
|
||||||
|
setValue(tab, key, val) {
|
||||||
|
Object.assign(this.$props.data[tab][key], { value: val, computed: val });
|
||||||
|
},
|
||||||
|
resetValue(tab, key) {
|
||||||
|
this.setValue(tab, key, this.$props.data[tab][key].default);
|
||||||
|
},
|
||||||
|
tabClass(tab) {
|
||||||
|
const classes = [];
|
||||||
|
if (tab === this.$data.active) classes.push('is-active');
|
||||||
|
if (this.changesCount(tab)) classes.push('is-changed');
|
||||||
|
const tabs = this.$data.tabs.map(t => t.key);
|
||||||
|
if (tabs.indexOf(tab) < tabs.indexOf(this.$data.active)) classes.push('is-before');
|
||||||
|
return classes.join(' ');
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
|
@ -0,0 +1,160 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div class="field is-horizontal is-aligned-top">
|
||||||
|
<div class="field-label">
|
||||||
|
<label class="label">SSL profile</label>
|
||||||
|
</div>
|
||||||
|
<div class="field-body">
|
||||||
|
<div class="field">
|
||||||
|
<div class="field">
|
||||||
|
<div v-for="(name, value) in $props.data.sslProfile.options"
|
||||||
|
:class="`control${sslProfileChanged ? ' is-changed' : ''}`"
|
||||||
|
>
|
||||||
|
<div class="radio">
|
||||||
|
<PrettyRadio v-model="sslProfile" :value="value" class="p-default p-round p-fill p-icon">
|
||||||
|
<i slot="extra" class="icon fas fa-check"></i>
|
||||||
|
{{ name }}
|
||||||
|
</PrettyRadio>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="field is-horizontal is-aligned-top">
|
||||||
|
<div class="field-label">
|
||||||
|
<label class="label">OCSP DNS Resolvers</label>
|
||||||
|
</div>
|
||||||
|
<div class="field-body">
|
||||||
|
<div class="field">
|
||||||
|
<div :class="`control${ocspCloudflareChanged ? ' is-changed' : ''}`">
|
||||||
|
<div class="checkbox">
|
||||||
|
<PrettyCheck v-model="ocspCloudflare" class="p-default p-curve p-fill p-icon">
|
||||||
|
<i slot="extra" class="icon fas fa-check"></i>
|
||||||
|
Cloudflare Resolver
|
||||||
|
</PrettyCheck>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div :class="`control${ocspGoogleChanged ? ' is-changed' : ''}`">
|
||||||
|
<div class="checkbox">
|
||||||
|
<PrettyCheck v-model="ocspGoogle" class="p-default p-curve p-fill p-icon">
|
||||||
|
<i slot="extra" class="icon fas fa-check"></i>
|
||||||
|
Google Public DNS
|
||||||
|
</PrettyCheck>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div :class="`control${ocspOpenDnsChanged ? ' is-changed' : ''}`">
|
||||||
|
<div class="checkbox">
|
||||||
|
<PrettyCheck v-model="ocspOpenDns" class="p-default p-curve p-fill p-icon">
|
||||||
|
<i slot="extra" class="icon fas fa-check"></i>
|
||||||
|
OpenDNS
|
||||||
|
</PrettyCheck>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="letsEncryptRootEnabled" class="field is-horizontal">
|
||||||
|
<div class="field-label">
|
||||||
|
<label class="label">Let's Encrypt webroot</label>
|
||||||
|
</div>
|
||||||
|
<div class="field-body">
|
||||||
|
<div class="field">
|
||||||
|
<div :class="`control${letsEncryptRootChanged ? ' is-changed' : ''}`">
|
||||||
|
<input v-model="letsEncryptRoot"
|
||||||
|
class="input"
|
||||||
|
type="text"
|
||||||
|
:placeholder="$props.data.letsEncryptRoot.default"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import PrettyCheck from 'pretty-checkbox-vue/check';
|
||||||
|
import PrettyRadio from 'pretty-checkbox-vue/radio';
|
||||||
|
import i18n from '../../i18n';
|
||||||
|
import delegatedFromDefaults from '../../util/delegated_from_defaults';
|
||||||
|
import computedFromDefaults from '../../util/computed_from_defaults';
|
||||||
|
|
||||||
|
const defaults = {
|
||||||
|
sslProfile: {
|
||||||
|
default: 'intermediate',
|
||||||
|
options: {
|
||||||
|
modern: 'Mozilla Modern',
|
||||||
|
intermediate: 'Mozilla Intermediate',
|
||||||
|
old: 'Mozilla Old',
|
||||||
|
},
|
||||||
|
enabled: true,
|
||||||
|
},
|
||||||
|
ocspCloudflare: {
|
||||||
|
default: true,
|
||||||
|
enabled: true,
|
||||||
|
},
|
||||||
|
ocspGoogle: {
|
||||||
|
default: true,
|
||||||
|
enabled: true,
|
||||||
|
},
|
||||||
|
ocspOpenDns: {
|
||||||
|
default: true,
|
||||||
|
enabled: true,
|
||||||
|
},
|
||||||
|
letsEncryptRoot: {
|
||||||
|
default: '/var/www/_letsencrypt/',
|
||||||
|
enabled: true,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'GlobalHTTPS', // Component name
|
||||||
|
display: 'HTTPS', // Display name for tab
|
||||||
|
key: 'https', // Key for data in parent
|
||||||
|
delegated: delegatedFromDefaults(defaults), // Data the parent will present here
|
||||||
|
components: {
|
||||||
|
PrettyCheck,
|
||||||
|
PrettyRadio,
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
data: Object, // Data delegated back to us from parent
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
i18n,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: computedFromDefaults(defaults, 'https'), // Getters & setters for the delegated data
|
||||||
|
watch: {
|
||||||
|
// Check SSL profile is valid
|
||||||
|
'$props.data.sslProfile': {
|
||||||
|
handler(data) {
|
||||||
|
// This might cause recursion, but seems not to
|
||||||
|
if (data.enabled)
|
||||||
|
if (!Object.keys(data.options).includes(data.computed))
|
||||||
|
data.computed = data.default;
|
||||||
|
},
|
||||||
|
deep: true,
|
||||||
|
},
|
||||||
|
// Enable LE webroot if any site uses LE
|
||||||
|
'$parent.$parent.$data.domains': {
|
||||||
|
handler(data) {
|
||||||
|
for (const domain of data) {
|
||||||
|
if (domain && domain.https && domain.https.certType
|
||||||
|
&& domain.https.certType.computed === 'letsEncrypt') {
|
||||||
|
this.$props.data.letsEncryptRoot.enabled = true;
|
||||||
|
this.$props.data.letsEncryptRoot.computed = this.$props.data.letsEncryptRoot.value;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.$props.data.letsEncryptRoot.enabled = false;
|
||||||
|
this.$props.data.letsEncryptRoot.computed = '';
|
||||||
|
},
|
||||||
|
deep: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
|
@ -0,0 +1 @@
|
||||||
|
export { default as HTTPS } from './https';
|
Loading…
Reference in New Issue