Global config base & https tab

pull/111/head
MattIPv4 2020-05-01 17:45:04 +01:00
parent 71622272b5
commit 5d3110d349
5 changed files with 279 additions and 2 deletions

View File

@ -1,5 +1,5 @@
export default {
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.',
};

View File

@ -27,6 +27,8 @@ limitations under the License.
</Header>
<div class="main container">
<h2>Per-website config</h2>
<div class="tabs">
<ul>
<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' }"
></Domain>
</template>
<h2>Global config</h2>
<Global :data="global"></Global>
</div>
<Footer :text="i18n.templates.app.oss"></Footer>
@ -60,13 +65,15 @@ limitations under the License.
import isChanged from '../util/is_changed';
import i18n from '../i18n';
import Domain from './domain';
import Global from './global';
export default {
name: 'App',
components: {
Domain,
Header,
Footer,
Domain,
Global,
},
data() {
return {
@ -74,6 +81,7 @@ limitations under the License.
domains: [
clone(Domain.delegated),
],
global: Global.delegated,
active: 0,
};
},

View File

@ -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>

View File

@ -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>

View File

@ -0,0 +1 @@
export { default as HTTPS } from './https';