From e7e9cbcfa26e5a4522baa720e21cb47b8492889f Mon Sep 17 00:00:00 2001 From: MattIPv4 <me@mattcowley.co.uk> Date: Mon, 4 May 2020 20:55:23 +0100 Subject: [PATCH] Add global tools section & export data generator --- src/nginxconfig/scss/style.scss | 24 +++ src/nginxconfig/templates/app.vue | 6 +- .../templates/global_sections/index.js | 1 + .../templates/global_sections/tools.vue | 192 ++++++++++++++++++ src/nginxconfig/util/export_data.js | 42 ++++ 5 files changed, 264 insertions(+), 1 deletion(-) create mode 100644 src/nginxconfig/templates/global_sections/tools.vue create mode 100644 src/nginxconfig/util/export_data.js diff --git a/src/nginxconfig/scss/style.scss b/src/nginxconfig/scss/style.scss index e31c5f2..4281af0 100644 --- a/src/nginxconfig/scss/style.scss +++ b/src/nginxconfig/scss/style.scss @@ -144,6 +144,18 @@ $highlight: #f2c94c; } } + &.is-grouped { + > .control { + &:last-child { + margin: .25rem 0 0; + } + + &:not(:last-child) { + margin: .25rem .75rem 0 0; + } + } + } + .is-changed { input { &:not(.vs__search) { @@ -208,6 +220,18 @@ $highlight: #f2c94c; } } + .field-body { + &.is-vertical { + flex-direction: column; + + > .field { + &:not(:last-child) { + margin-bottom: .75rem; + } + } + } + } + .checkbox, .radio { border-radius: $border-radius; diff --git a/src/nginxconfig/templates/app.vue b/src/nginxconfig/templates/app.vue index 71cfae0..8299f80 100644 --- a/src/nginxconfig/templates/app.vue +++ b/src/nginxconfig/templates/app.vue @@ -53,7 +53,7 @@ limitations under the License. <h2>Global config</h2> <Global :data="global"></Global> - <pre><code>{{ JSON.stringify({ domains: activeDomains, global }, null, 2) }}</code></pre> + <pre><code>{{ exportData }}</code></pre> </div> <Footer :text="i18n.templates.app.oss"></Footer> @@ -65,6 +65,7 @@ limitations under the License. import Header from 'do-vue/src/templates/header'; import Footer from 'do-vue/src/templates/footer'; import isChanged from '../util/is_changed'; + import exportData from '../util/export_data'; import i18n from '../i18n'; import Domain from './domain'; import Global from './global'; @@ -91,6 +92,9 @@ limitations under the License. activeDomains() { return this.$data.domains.map((domain, index) => [domain, index]).filter(d => d[0] !== null); }, + exportData() { + return JSON.stringify(exportData(this.activeDomains, this.$data.global), null, 2); + }, }, methods: { changes(index) { diff --git a/src/nginxconfig/templates/global_sections/index.js b/src/nginxconfig/templates/global_sections/index.js index 9cf6c4f..1a58387 100644 --- a/src/nginxconfig/templates/global_sections/index.js +++ b/src/nginxconfig/templates/global_sections/index.js @@ -5,3 +5,4 @@ export { default as Python } from './python'; export { default as Performance } from './performance'; export { default as Logging } from './logging'; export { default as NGINX } from './nginx'; +export { default as Tools } from './tools'; diff --git a/src/nginxconfig/templates/global_sections/tools.vue b/src/nginxconfig/templates/global_sections/tools.vue new file mode 100644 index 0000000..2497989 --- /dev/null +++ b/src/nginxconfig/templates/global_sections/tools.vue @@ -0,0 +1,192 @@ +<template> + <div> + <div class="field is-horizontal"> + <div class="field-label"> + <label class="label">Modularized structure</label> + </div> + <div class="field-body"> + <div class="field"> + <div :class="`control${modularizedStructureChanged ? ' is-changed' : ''}`"> + <div class="checkbox"> + <PrettyCheck v-model="modularizedStructure" class="p-default p-curve p-fill p-icon"> + <i slot="extra" class="icon fas fa-check"></i> + enable modularized config files + </PrettyCheck> + </div> + </div> + </div> + </div> + </div> + + <div class="field is-horizontal"> + <div class="field-label"> + <label class="label">Symlink vhost</label> + </div> + <div class="field-body"> + <div class="field"> + <div :class="`control${symlinkVhostChanged ? ' is-changed' : ''}`"> + <div class="checkbox"> + <PrettyCheck v-model="symlinkVhost" class="p-default p-curve p-fill p-icon"> + <i slot="extra" class="icon fas fa-check"></i> + enable symlink from sites-available/ to sites-enabled/ + </PrettyCheck> + </div> + </div> + </div> + </div> + </div> + + <div class="field is-horizontal"> + <div class="field-label"> + <label class="label">Share configuration</label> + </div> + <div class="field-body"> + <div class="field"> + <div class="control"> + <input v-model="shareLink" + class="input" + type="text" + disabled="disabled" + /> + </div> + </div> + </div> + </div> + + <div class="field is-horizontal"> + <div class="field-label"> + <label class="label">Reset configuration</label> + </div> + <div class="field-body"> + <div class="field is-grouped"> + <div class="control"> + <a class="button is-danger is-mini" @click="resetGlobal"> + Reset global config + </a> + </div> + <div v-if="hasDomain" class="control"> + <a class="button is-danger is-mini" @click="resetDomains"> + Reset all domains + </a> + </div> + <div v-if="hasDomain" class="control"> + <a class="button is-danger is-mini" @click="removeDomains"> + Remove all domains + </a> + </div> + </div> + </div> + </div> + + <div class="field is-horizontal"> + <div class="field-label"> + </div> + <div class="field-body is-vertical"> + <div v-for="domainData in $parent.$parent.activeDomains" class="field is-horizontal"> + <div class="field-label"> + <label class="label">{{ domainData[0].server.domain.computed }}</label> + </div> + <div class="field-body"> + <div class="field is-grouped"> + <div class="control"> + <a class="button is-danger is-mini" @click="resetDomain(domainData[1])"> + Reset domain config + </a> + </div> + <div class="control"> + <a class="button is-danger is-mini" @click="removeDomain(domainData[1])"> + Remove domain + </a> + </div> + </div> + </div> + </div> + </div> + </div> + </div> +</template> + +<script> + import PrettyCheck from 'pretty-checkbox-vue/check'; + import i18n from '../../i18n'; + import delegatedFromDefaults from '../../util/delegated_from_defaults'; + import computedFromDefaults from '../../util/computed_from_defaults'; + import exportData from '../../util/export_data'; + + const defaults = { + modularizedStructure: { + default: true, + enabled: true, + }, + symlinkVhost: { + default: true, + enabled: true, + }, + }; + + export default { + name: 'GlobalTools', // Component name + display: 'Tools', // Display name for tab + key: 'tools', // Key for data in parent + delegated: delegatedFromDefaults(defaults), // Data the parent will present here + components: { + PrettyCheck, + }, + props: { + data: Object, // Data delegated back to us from parent + }, + data () { + return { + i18n, + }; + }, + computed: { + ...computedFromDefaults(defaults, 'tools'), // Getters & setters for the delegated data + hasDomain() { + return this.$parent.$parent.activeDomains.length > 0; + }, + shareLink() { + return JSON.stringify(exportData(this.$parent.$parent.activeDomains, this.$parent.$props.data)); + }, + }, + methods: { + // TODO: These all need confirmation prompts! + resetGlobal() { + Object.values(this.$parent.$props.data).forEach(category => { + Object.values(category).forEach(property => { + property.value = property.default; + property.computed = property.default; + }); + }); + }, + resetDomain(index) { + if (index >= this.$parent.$parent.$data.domains.length) return; + + const domain = this.$parent.$parent.$data.domains[index]; + if (!domain) return; + + Object.values(domain).forEach(category => { + Object.values(category).forEach(property => { + property.value = property.default; + property.computed = property.default; + }); + }); + }, + removeDomain(index) { + if (index >= this.$parent.$parent.$data.domains.length) return; + + this.$set(this.$parent.$parent.$data.domains, index, null); + }, + resetDomains() { + for (let i = 0; i < this.$parent.$parent.$data.domains.length; i++) { + this.resetDomain(i); + } + }, + removeDomains() { + for (let i = 0; i < this.$parent.$parent.$data.domains.length; i++) { + this.removeDomain(i); + } + }, + }, + }; +</script> diff --git a/src/nginxconfig/util/export_data.js b/src/nginxconfig/util/export_data.js new file mode 100644 index 0000000..d4243b2 --- /dev/null +++ b/src/nginxconfig/util/export_data.js @@ -0,0 +1,42 @@ +const categoriesExport = (categories) => { + const categoriesData = {}; + + // Work through each category + for (const category in categories) { + const categoryData = {}; + + // Go over each property in the category + for (const property in categories[category]) { + + // If the user input differs from the default, they changed it, so we save it + const propertyData = categories[category][property]; + if (propertyData.value !== propertyData.default) { + categoryData[property] = propertyData.value; + } + } + + // If there were any property changes, save the category + if (Object.keys(categoryData).length) { + categoriesData[category] = categoryData; + } + } + + return categoriesData; +}; + +export default (domains, global) => { + const exportData = {}; + + // Handle domains + // Always save changes, even if none, so we can replicate the correct number of domains + exportData.domains = domains.map(domain => categoriesExport(domain[0])); + + // Handle global + // If there were any category changes, save global changes + const globalData = categoriesExport(global); + if (Object.keys(globalData).length) { + exportData.global = globalData; + } + + return exportData; +};