Refactor analytics events (#209)
* Refactor analytics.js * Update analytics calls in app.vue * Update analytics calls in presets.vue * Update analytics calls in tools.vue (and app.vue) * Update analytics calls in global.vue * Update analytics calls in domain.vue * Update analytics calls in setup.vue * Add list of all events to analytics.js * Add custom copy to clipboard that emits event * Emit the events from the components * Update copyright year in all files touched * Update analytics calls in download.vue * Update analytics calls in ssl.vue * Update analytics calls in certbot.vue * Update analytics calls in domain.vue * Update analytics calls in app.vue * Note down 'Code snippet copied' eventspull/214/head
parent
c86fb3cf76
commit
3fdccfa68a
|
@ -1,5 +1,5 @@
|
||||||
<!--
|
<!--
|
||||||
Copyright 2020 DigitalOcean
|
Copyright 2021 DigitalOcean
|
||||||
|
|
||||||
This code is licensed under the MIT License.
|
This code is licensed under the MIT License.
|
||||||
You may obtain a copy of the License at
|
You may obtain a copy of the License at
|
||||||
|
@ -101,6 +101,7 @@ THE SOFTWARE.
|
||||||
:name="confContents[0]"
|
:name="confContents[0]"
|
||||||
:conf="confContents[1]"
|
:conf="confContents[1]"
|
||||||
:half="Object.keys(confFilesOutput).length > 1 && !splitColumn"
|
:half="Object.keys(confFilesOutput).length > 1 && !splitColumn"
|
||||||
|
@copied="codeCopiedEvent(confContents[3])"
|
||||||
></component>
|
></component>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
|
@ -125,7 +126,6 @@ THE SOFTWARE.
|
||||||
import isObject from '../util/is_object';
|
import isObject from '../util/is_object';
|
||||||
import analytics from '../util/analytics';
|
import analytics from '../util/analytics';
|
||||||
import browserLanguage from '../util/browser_language';
|
import browserLanguage from '../util/browser_language';
|
||||||
import { toSep } from '../util/language_pack_name';
|
|
||||||
import { defaultPack } from '../util/language_pack_default';
|
import { defaultPack } from '../util/language_pack_default';
|
||||||
import { availablePacks } from '../util/language_pack_context';
|
import { availablePacks } from '../util/language_pack_context';
|
||||||
|
|
||||||
|
@ -149,8 +149,8 @@ THE SOFTWARE.
|
||||||
Global,
|
Global,
|
||||||
Setup,
|
Setup,
|
||||||
NginxPrism,
|
NginxPrism,
|
||||||
'YamlPrism': () => import('./prism/yaml'),
|
YamlPrism: () => import('./prism/yaml'),
|
||||||
'DockerPrism': () => import('./prism/docker'),
|
DockerPrism: () => import('./prism/docker'),
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
@ -171,9 +171,10 @@ THE SOFTWARE.
|
||||||
splitColumn: false,
|
splitColumn: false,
|
||||||
confWatcherWaiting: false,
|
confWatcherWaiting: false,
|
||||||
confFilesPrevious: {},
|
confFilesPrevious: {},
|
||||||
confFilesOutput: {},
|
confFilesOutput: [],
|
||||||
languageLoading: false,
|
languageLoading: false,
|
||||||
languagePrevious: defaultPack,
|
languagePrevious: defaultPack,
|
||||||
|
interactiveEvents: false,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
@ -214,8 +215,12 @@ THE SOFTWARE.
|
||||||
},
|
},
|
||||||
'$data.global.app.lang': {
|
'$data.global.app.lang': {
|
||||||
handler(data) {
|
handler(data) {
|
||||||
|
// Lock out the dropdown
|
||||||
this.$data.languageLoading = true;
|
this.$data.languageLoading = true;
|
||||||
|
|
||||||
|
// Store if we should fire the event when this is done loading
|
||||||
|
const interactive = this.$data.interactiveEvents;
|
||||||
|
|
||||||
// Ensure valid pack
|
// Ensure valid pack
|
||||||
if (!availablePacks.includes(data.value)) data.computed = data.default;
|
if (!availablePacks.includes(data.value)) data.computed = data.default;
|
||||||
|
|
||||||
|
@ -227,7 +232,7 @@ THE SOFTWARE.
|
||||||
this.$data.languageLoading = false;
|
this.$data.languageLoading = false;
|
||||||
|
|
||||||
// Analytics
|
// Analytics
|
||||||
analytics(`set_language_${toSep(data.computed, '_')}`, 'Language');
|
this.languageSetEvent(!interactive);
|
||||||
}).catch((err) => {
|
}).catch((err) => {
|
||||||
// Error
|
// Error
|
||||||
console.log('Failed to set language to', data.computed);
|
console.log('Failed to set language to', data.computed);
|
||||||
|
@ -255,8 +260,10 @@ THE SOFTWARE.
|
||||||
if (language) this.lang = language;
|
if (language) this.lang = language;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send an initial GA event for column mode
|
// Initial analytics events
|
||||||
this.splitColumnEvent(true);
|
this.splitColumnEvent(true);
|
||||||
|
for (let i = 0; i < this.activeDomains.length; i++) this.addSiteEvent(i + 1, true);
|
||||||
|
this.$data.interactiveEvents = true;
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
changes(index) {
|
changes(index) {
|
||||||
|
@ -285,15 +292,16 @@ THE SOFTWARE.
|
||||||
this.$data.domains.push(data);
|
this.$data.domains.push(data);
|
||||||
this.$data.active = this.$data.domains.length - 1;
|
this.$data.active = this.$data.domains.length - 1;
|
||||||
|
|
||||||
// GA
|
// Analytics
|
||||||
analytics('add_site', 'Sites', undefined, this.activeDomains.length);
|
this.addSiteEvent(this.activeDomains.length);
|
||||||
},
|
},
|
||||||
remove(index) {
|
remove(index) {
|
||||||
|
const name = this.$data.domains[index].server.domain.computed;
|
||||||
this.$set(this.$data.domains, index, null);
|
this.$set(this.$data.domains, index, null);
|
||||||
if (this.$data.active === index) this.$data.active = this.$data.domains.findIndex(d => d !== null);
|
if (this.$data.active === index) this.$data.active = this.$data.domains.findIndex(d => d !== null);
|
||||||
|
|
||||||
// GA
|
// Analytics
|
||||||
analytics('remove_site', 'Sites', undefined, this.activeDomains.length);
|
this.removeSiteEvent(this.activeDomains.length, name);
|
||||||
},
|
},
|
||||||
checkChange(oldConf) {
|
checkChange(oldConf) {
|
||||||
// If nothing has changed for a tick, we can use the config files
|
// If nothing has changed for a tick, we can use the config files
|
||||||
|
@ -319,7 +327,7 @@ THE SOFTWARE.
|
||||||
const diffConf = diff(newConf, oldConf, {
|
const diffConf = diff(newConf, oldConf, {
|
||||||
highlightFunction: value => `<mark>${value}</mark>`,
|
highlightFunction: value => `<mark>${value}</mark>`,
|
||||||
});
|
});
|
||||||
this.$data.confFilesOutput = Object.values(diffConf).map(({ name, content }) => {
|
this.$data.confFilesOutput = Object.entries(diffConf).map(([ file, { name, content } ]) => {
|
||||||
const diffName = name.filter(x => !x.removed).map(x => x.value).join('');
|
const diffName = name.filter(x => !x.removed).map(x => x.value).join('');
|
||||||
const confName = `${escape(this.$data.global.nginx.nginxConfigDirectory.computed)}/${diffName}`;
|
const confName = `${escape(this.$data.global.nginx.nginxConfigDirectory.computed)}/${diffName}`;
|
||||||
const diffContent = content.filter(x => !x.removed).map(x => x.value).join('');
|
const diffContent = content.filter(x => !x.removed).map(x => x.value).join('');
|
||||||
|
@ -328,6 +336,7 @@ THE SOFTWARE.
|
||||||
confName,
|
confName,
|
||||||
diffContent,
|
diffContent,
|
||||||
`${sha2_256(confName)}-${sha2_256(diffContent)}`,
|
`${sha2_256(confName)}-${sha2_256(diffContent)}`,
|
||||||
|
file,
|
||||||
];
|
];
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
@ -339,6 +348,7 @@ THE SOFTWARE.
|
||||||
confName,
|
confName,
|
||||||
content,
|
content,
|
||||||
`${sha2_256(confName)}-${sha2_256(content)}`,
|
`${sha2_256(confName)}-${sha2_256(content)}`,
|
||||||
|
name,
|
||||||
];
|
];
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -351,7 +361,42 @@ THE SOFTWARE.
|
||||||
this.splitColumnEvent();
|
this.splitColumnEvent();
|
||||||
},
|
},
|
||||||
splitColumnEvent(nonInteraction = false) {
|
splitColumnEvent(nonInteraction = false) {
|
||||||
analytics('toggle_split_column', 'Button', undefined, Number(this.$data.splitColumn), nonInteraction);
|
analytics({
|
||||||
|
category: 'Split column',
|
||||||
|
action: this.$data.splitColumn ? 'Enabled' : 'Disabled',
|
||||||
|
nonInteraction,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
languageSetEvent(nonInteraction = false) {
|
||||||
|
analytics({
|
||||||
|
category: 'Language',
|
||||||
|
action: 'Set',
|
||||||
|
label: this.$data.global.app.lang.computed,
|
||||||
|
nonInteraction,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
addSiteEvent(count, nonInteraction = false) {
|
||||||
|
analytics({
|
||||||
|
category: 'Site',
|
||||||
|
action: 'Added',
|
||||||
|
value: count,
|
||||||
|
nonInteraction,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
removeSiteEvent(count, name) {
|
||||||
|
analytics({
|
||||||
|
category: 'Site',
|
||||||
|
action: 'Removed',
|
||||||
|
label: name,
|
||||||
|
value: count,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
codeCopiedEvent(file) {
|
||||||
|
analytics({
|
||||||
|
category: 'Config files',
|
||||||
|
action: 'Code snippet copied',
|
||||||
|
label: file,
|
||||||
|
});
|
||||||
},
|
},
|
||||||
getPrismComponent(confName) {
|
getPrismComponent(confName) {
|
||||||
switch (confName) {
|
switch (confName) {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<!--
|
<!--
|
||||||
Copyright 2020 DigitalOcean
|
Copyright 2021 DigitalOcean
|
||||||
|
|
||||||
This code is licensed under the MIT License.
|
This code is licensed under the MIT License.
|
||||||
You may obtain a copy of the License at
|
You may obtain a copy of the License at
|
||||||
|
@ -34,7 +34,7 @@ THE SOFTWARE.
|
||||||
<div class="tabs">
|
<div class="tabs">
|
||||||
<ul>
|
<ul>
|
||||||
<li v-for="tab in tabs" :class="tabClass(tab.key)">
|
<li v-for="tab in tabs" :class="tabClass(tab.key)">
|
||||||
<a @click="active = tab.key">{{ $t(tab.display) }}{{ changes(tab.key) }}</a>
|
<a @click="showTab(tab.key)">{{ $t(tab.display) }}{{ changes(tab.key) }}</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
@ -48,10 +48,10 @@ THE SOFTWARE.
|
||||||
></component>
|
></component>
|
||||||
|
|
||||||
<div class="navigation-buttons">
|
<div class="navigation-buttons">
|
||||||
<a v-if="previousTab !== false" class="button is-mini" @click="active = previousTab">
|
<a v-if="previousTab !== false" class="button is-mini" @click="showPreviousTab">
|
||||||
<i class="fas fa-long-arrow-alt-left"></i> <span>{{ $t('common.back') }}</span>
|
<i class="fas fa-long-arrow-alt-left"></i> <span>{{ $t('common.back') }}</span>
|
||||||
</a>
|
</a>
|
||||||
<a v-if="nextTab !== false" class="button is-primary is-mini" @click="active = nextTab">
|
<a v-if="nextTab !== false" class="button is-primary is-mini" @click="showNextTab">
|
||||||
<span>{{ $t('common.next') }}</span> <i class="fas fa-long-arrow-alt-right"></i>
|
<span>{{ $t('common.next') }}</span> <i class="fas fa-long-arrow-alt-right"></i>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
@ -60,6 +60,7 @@ THE SOFTWARE.
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import analytics from '../util/analytics';
|
||||||
import isChanged from '../util/is_changed';
|
import isChanged from '../util/is_changed';
|
||||||
import Presets from './domain_sections/presets';
|
import Presets from './domain_sections/presets';
|
||||||
import * as Sections from './domain_sections';
|
import * as Sections from './domain_sections';
|
||||||
|
@ -127,6 +128,39 @@ THE SOFTWARE.
|
||||||
if (tabs.indexOf(tab) < tabs.indexOf(this.$data.active)) classes.push('is-before');
|
if (tabs.indexOf(tab) < tabs.indexOf(this.$data.active)) classes.push('is-before');
|
||||||
return classes.join(' ');
|
return classes.join(' ');
|
||||||
},
|
},
|
||||||
|
showTab(target) {
|
||||||
|
// Analytics
|
||||||
|
analytics({
|
||||||
|
category: 'Site',
|
||||||
|
action: 'Tab clicked',
|
||||||
|
label: `${this.$data.active}, ${target}`,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Go!
|
||||||
|
this.$data.active = target;
|
||||||
|
},
|
||||||
|
showPreviousTab() {
|
||||||
|
// Analytics
|
||||||
|
analytics({
|
||||||
|
category: 'Site',
|
||||||
|
action: 'Back clicked',
|
||||||
|
label: `${this.$data.active}, ${this.previousTab}`,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Go!
|
||||||
|
this.$data.active = this.previousTab;
|
||||||
|
},
|
||||||
|
showNextTab() {
|
||||||
|
// Analytics
|
||||||
|
analytics({
|
||||||
|
category: 'Site',
|
||||||
|
action: 'Next clicked',
|
||||||
|
label: `${this.$data.active}, ${this.nextTab}`,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Go!
|
||||||
|
this.$data.active = this.nextTab;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<!--
|
<!--
|
||||||
Copyright 2020 DigitalOcean
|
Copyright 2021 DigitalOcean
|
||||||
|
|
||||||
This code is licensed under the MIT License.
|
This code is licensed under the MIT License.
|
||||||
You may obtain a copy of the License at
|
You may obtain a copy of the License at
|
||||||
|
@ -56,7 +56,7 @@ THE SOFTWARE.
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<div class="control" v-if="incorrectEnding">
|
<div v-if="incorrectEnding" class="control">
|
||||||
<label class="text message is-warning">
|
<label class="text message is-warning">
|
||||||
<span class="message-body">
|
<span class="message-body">
|
||||||
{{ $t('templates.domainSections.onion.onionLocationExpectedToEndWithOnion') }}
|
{{ $t('templates.domainSections.onion.onionLocationExpectedToEndWithOnion') }}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<!--
|
<!--
|
||||||
Copyright 2020 DigitalOcean
|
Copyright 2021 DigitalOcean
|
||||||
|
|
||||||
This code is licensed under the MIT License.
|
This code is licensed under the MIT License.
|
||||||
You may obtain a copy of the License at
|
You may obtain a copy of the License at
|
||||||
|
@ -56,7 +56,6 @@ THE SOFTWARE.
|
||||||
import delegatedFromDefaults from '../../util/delegated_from_defaults';
|
import delegatedFromDefaults from '../../util/delegated_from_defaults';
|
||||||
import computedFromDefaults from '../../util/computed_from_defaults';
|
import computedFromDefaults from '../../util/computed_from_defaults';
|
||||||
import analytics from '../../util/analytics';
|
import analytics from '../../util/analytics';
|
||||||
import camelToSnake from '../../util/camel_to_snake';
|
|
||||||
|
|
||||||
const defaults = {
|
const defaults = {
|
||||||
frontend: {
|
frontend: {
|
||||||
|
@ -208,7 +207,7 @@ THE SOFTWARE.
|
||||||
setPreset(key) {
|
setPreset(key) {
|
||||||
// Set that we're using this preset
|
// Set that we're using this preset
|
||||||
Object.keys(this.$props.data).forEach(preset => this[preset] = preset === key);
|
Object.keys(this.$props.data).forEach(preset => this[preset] = preset === key);
|
||||||
analytics(`apply_${camelToSnake(key)}`, 'Presets');
|
this.presetEvent(key, this.interacted);
|
||||||
|
|
||||||
// Restore some specific defaults first
|
// Restore some specific defaults first
|
||||||
this.$parent.resetValue('server', 'domain');
|
this.$parent.resetValue('server', 'domain');
|
||||||
|
@ -271,6 +270,13 @@ THE SOFTWARE.
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
presetEvent(name, overwrite = false) {
|
||||||
|
analytics({
|
||||||
|
category: 'Preset',
|
||||||
|
action: overwrite ? 'Overwritten' : 'Applied', // TODO: Is overwritten the best word here?
|
||||||
|
label: name,
|
||||||
|
});
|
||||||
|
},
|
||||||
toggleCollapse() {
|
toggleCollapse() {
|
||||||
if (this.interacted) {
|
if (this.interacted) {
|
||||||
this.expanded = !this.expanded;
|
this.expanded = !this.expanded;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<!--
|
<!--
|
||||||
Copyright 2020 DigitalOcean
|
Copyright 2021 DigitalOcean
|
||||||
|
|
||||||
This code is licensed under the MIT License.
|
This code is licensed under the MIT License.
|
||||||
You may obtain a copy of the License at
|
You may obtain a copy of the License at
|
||||||
|
@ -29,7 +29,7 @@ THE SOFTWARE.
|
||||||
<div class="tabs">
|
<div class="tabs">
|
||||||
<ul>
|
<ul>
|
||||||
<li v-for="tab in tabs" :class="tabClass(tab.key)">
|
<li v-for="tab in tabs" :class="tabClass(tab.key)">
|
||||||
<a @click="active = tab.key">{{ $t(tab.display) }}{{ changes(tab.key) }}</a>
|
<a @click="showTab(tab.key)">{{ $t(tab.display) }}{{ changes(tab.key) }}</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
@ -43,10 +43,10 @@ THE SOFTWARE.
|
||||||
></component>
|
></component>
|
||||||
|
|
||||||
<div class="navigation-buttons">
|
<div class="navigation-buttons">
|
||||||
<a v-if="previousTab !== false" class="button is-mini" @click="active = previousTab">
|
<a v-if="previousTab !== false" class="button is-mini" @click="showPreviousTab">
|
||||||
<i class="fas fa-long-arrow-alt-left"></i> <span>{{ $t('common.back') }}</span>
|
<i class="fas fa-long-arrow-alt-left"></i> <span>{{ $t('common.back') }}</span>
|
||||||
</a>
|
</a>
|
||||||
<a v-if="nextTab !== false" class="button is-primary is-mini" @click="active = nextTab">
|
<a v-if="nextTab !== false" class="button is-primary is-mini" @click="showNextTab">
|
||||||
<span>{{ $t('common.next') }}</span> <i class="fas fa-long-arrow-alt-right"></i>
|
<span>{{ $t('common.next') }}</span> <i class="fas fa-long-arrow-alt-right"></i>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
@ -54,6 +54,7 @@ THE SOFTWARE.
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import analytics from '../util/analytics';
|
||||||
import isChanged from '../util/is_changed';
|
import isChanged from '../util/is_changed';
|
||||||
import * as Sections from './global_sections';
|
import * as Sections from './global_sections';
|
||||||
|
|
||||||
|
@ -113,6 +114,39 @@ THE SOFTWARE.
|
||||||
if (tabs.indexOf(tab) < tabs.indexOf(this.$data.active)) classes.push('is-before');
|
if (tabs.indexOf(tab) < tabs.indexOf(this.$data.active)) classes.push('is-before');
|
||||||
return classes.join(' ');
|
return classes.join(' ');
|
||||||
},
|
},
|
||||||
|
showTab(target) {
|
||||||
|
// Analytics
|
||||||
|
analytics({
|
||||||
|
category: 'Global',
|
||||||
|
action: 'Tab clicked',
|
||||||
|
label: `${this.$data.active}, ${target}`,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Go!
|
||||||
|
this.$data.active = target;
|
||||||
|
},
|
||||||
|
showPreviousTab() {
|
||||||
|
// Analytics
|
||||||
|
analytics({
|
||||||
|
category: 'Global',
|
||||||
|
action: 'Back clicked',
|
||||||
|
label: `${this.$data.active}, ${this.previousTab}`,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Go!
|
||||||
|
this.$data.active = this.previousTab;
|
||||||
|
},
|
||||||
|
showNextTab() {
|
||||||
|
// Analytics
|
||||||
|
analytics({
|
||||||
|
category: 'Global',
|
||||||
|
action: 'Next clicked',
|
||||||
|
label: `${this.$data.active}, ${this.nextTab}`,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Go!
|
||||||
|
this.$data.active = this.nextTab;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<!--
|
<!--
|
||||||
Copyright 2020 DigitalOcean
|
Copyright 2021 DigitalOcean
|
||||||
|
|
||||||
This code is licensed under the MIT License.
|
This code is licensed under the MIT License.
|
||||||
You may obtain a copy of the License at
|
You may obtain a copy of the License at
|
||||||
|
@ -243,7 +243,10 @@ THE SOFTWARE.
|
||||||
this.$t('templates.globalSections.tools.resetGlobalConfig'),
|
this.$t('templates.globalSections.tools.resetGlobalConfig'),
|
||||||
this.$t('templates.globalSections.tools.resetGlobalConfigBody'),
|
this.$t('templates.globalSections.tools.resetGlobalConfigBody'),
|
||||||
() => {
|
() => {
|
||||||
analytics('reset_global', 'Reset');
|
// Analytics
|
||||||
|
this.resetGlobalEvent();
|
||||||
|
|
||||||
|
// Do the reset
|
||||||
Object.values(this.$parent.$props.data).forEach(category => {
|
Object.values(this.$parent.$props.data).forEach(category => {
|
||||||
Object.values(category).forEach(property => {
|
Object.values(category).forEach(property => {
|
||||||
property.value = property.default;
|
property.value = property.default;
|
||||||
|
@ -264,7 +267,10 @@ THE SOFTWARE.
|
||||||
${domain.server.domain.computed}
|
${domain.server.domain.computed}
|
||||||
${this.$t('templates.globalSections.tools.domain')}`,
|
${this.$t('templates.globalSections.tools.domain')}`,
|
||||||
() => {
|
() => {
|
||||||
analytics('reset_domain', 'Reset', domain.server.domain.computed);
|
// Analytics
|
||||||
|
this.resetDomainEvent(domain.server.domain.computed);
|
||||||
|
|
||||||
|
// Do the reset
|
||||||
this.doResetDomain(domain);
|
this.doResetDomain(domain);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
@ -280,13 +286,10 @@ THE SOFTWARE.
|
||||||
${domain.server.domain.computed}
|
${domain.server.domain.computed}
|
||||||
${this.$t('templates.globalSections.tools.domainConfiguration')}`,
|
${this.$t('templates.globalSections.tools.domainConfiguration')}`,
|
||||||
() => {
|
() => {
|
||||||
analytics(
|
// Analytics
|
||||||
'remove_domain',
|
this.removeDomainEvent(domain.server.domain.computed);
|
||||||
'Remove',
|
|
||||||
domain.server.domain.computed,
|
|
||||||
this.$parent.$parent.activeDomains.length - 1,
|
|
||||||
);
|
|
||||||
|
|
||||||
|
// Do the removal
|
||||||
this.doRemoveDomain(index);
|
this.doRemoveDomain(index);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
@ -296,13 +299,13 @@ THE SOFTWARE.
|
||||||
this.$t('templates.globalSections.tools.resetAllDomainsConfig'),
|
this.$t('templates.globalSections.tools.resetAllDomainsConfig'),
|
||||||
this.$t('templates.globalSections.tools.resetAllDomainsConfigBody'),
|
this.$t('templates.globalSections.tools.resetAllDomainsConfigBody'),
|
||||||
() => {
|
() => {
|
||||||
analytics(
|
// Analytics
|
||||||
'reset_all',
|
this.resetDomainsEvent(
|
||||||
'Reset',
|
this.$parent.$parent.activeDomains.map(x => x[0].server.domain.computed),
|
||||||
this.$parent.$parent.activeDomains.map(x => x[0].server.domain.computed).join(','),
|
|
||||||
this.$parent.$parent.activeDomains.length,
|
this.$parent.$parent.activeDomains.length,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Do the reset
|
||||||
for (let i = 0; i < this.$parent.$parent.$data.domains.length; i++) {
|
for (let i = 0; i < this.$parent.$parent.$data.domains.length; i++) {
|
||||||
this.doResetDomain(this.$parent.$parent.$data.domains[i]);
|
this.doResetDomain(this.$parent.$parent.$data.domains[i]);
|
||||||
}
|
}
|
||||||
|
@ -314,19 +317,62 @@ THE SOFTWARE.
|
||||||
this.$t('templates.globalSections.tools.removeAllDomains'),
|
this.$t('templates.globalSections.tools.removeAllDomains'),
|
||||||
this.$t('templates.globalSections.tools.removeAllDomainsBody'),
|
this.$t('templates.globalSections.tools.removeAllDomainsBody'),
|
||||||
() => {
|
() => {
|
||||||
analytics(
|
// Analytics
|
||||||
'remove_all',
|
this.removeDomainsEvent(
|
||||||
'Remove',
|
this.$parent.$parent.activeDomains.map(x => x[0].server.domain.computed),
|
||||||
this.$parent.$parent.activeDomains.map(x => x[0].server.domain.computed).join(','),
|
|
||||||
this.$parent.$parent.activeDomains.length,
|
this.$parent.$parent.activeDomains.length,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Do the removal
|
||||||
for (let i = 0; i < this.$parent.$parent.$data.domains.length; i++) {
|
for (let i = 0; i < this.$parent.$parent.$data.domains.length; i++) {
|
||||||
this.doRemoveDomain(i);
|
this.doRemoveDomain(i);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
resetGlobalEvent() {
|
||||||
|
analytics({
|
||||||
|
category: 'Tools',
|
||||||
|
action: 'Global settings reset',
|
||||||
|
});
|
||||||
|
},
|
||||||
|
resetDomainEvent(name) {
|
||||||
|
analytics({
|
||||||
|
category: 'Tools',
|
||||||
|
action: 'Site reset',
|
||||||
|
label: name,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
removeDomainEvent(name) {
|
||||||
|
analytics({
|
||||||
|
category: 'Tools',
|
||||||
|
action: 'Removed site',
|
||||||
|
label: name,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Also fire the general site removal event
|
||||||
|
this.$parent.$parent.removeSiteEvent(this.$parent.$parent.activeDomains.length - 1, name);
|
||||||
|
},
|
||||||
|
resetDomainsEvent(names, count) {
|
||||||
|
analytics({
|
||||||
|
category: 'Tools',
|
||||||
|
action: 'All sites reset',
|
||||||
|
label: names.join(', '),
|
||||||
|
value: count,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
removeDomainsEvent(names, count) {
|
||||||
|
analytics({
|
||||||
|
category: 'Tools',
|
||||||
|
action: 'All sites removed',
|
||||||
|
label: names.join(', '),
|
||||||
|
value: count,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Also fire the general site removal event
|
||||||
|
for (let i = 0; i < this.$parent.$parent.$data.domains.length; i++)
|
||||||
|
this.$parent.$parent.removeSiteEvent(this.$parent.$parent.activeDomains.length - i - 1, names[i]);
|
||||||
|
},
|
||||||
select(event) {
|
select(event) {
|
||||||
event.target.setSelectionRange(0, event.target.value.length);
|
event.target.setSelectionRange(0, event.target.value.length);
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<!--
|
<!--
|
||||||
Copyright 2020 DigitalOcean
|
Copyright 2021 DigitalOcean
|
||||||
|
|
||||||
This code is licensed under the MIT License.
|
This code is licensed under the MIT License.
|
||||||
You may obtain a copy of the License at
|
You may obtain a copy of the License at
|
||||||
|
@ -25,7 +25,7 @@ THE SOFTWARE.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div @copied="copied">
|
||||||
<pre><code class="language-bash">{{ cmd }}</code></pre>
|
<pre><code class="language-bash">{{ cmd }}</code></pre>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
@ -42,5 +42,10 @@ THE SOFTWARE.
|
||||||
console.info(`Highlighting ${this.$props.cmd}...`);
|
console.info(`Highlighting ${this.$props.cmd}...`);
|
||||||
Prism.highlightAllUnder(this.$el);
|
Prism.highlightAllUnder(this.$el);
|
||||||
},
|
},
|
||||||
|
methods: {
|
||||||
|
copied(event) {
|
||||||
|
this.$emit('copied', event.detail.text);
|
||||||
|
},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<!--
|
<!--
|
||||||
Copyright 2020 DigitalOcean
|
Copyright 2021 DigitalOcean
|
||||||
|
|
||||||
This code is licensed under the MIT License.
|
This code is licensed under the MIT License.
|
||||||
You may obtain a copy of the License at
|
You may obtain a copy of the License at
|
||||||
|
@ -25,7 +25,7 @@ THE SOFTWARE.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div :class="`column ${half ? 'is-half' : 'is-full'} is-full-mobile is-full-tablet`">
|
<div :class="`column ${half ? 'is-half' : 'is-full'} is-full-mobile is-full-tablet`" @copied="copied">
|
||||||
<h3 v-html="name"></h3>
|
<h3 v-html="name"></h3>
|
||||||
<pre><code class="language-docker" v-html="conf"></code></pre>
|
<pre><code class="language-docker" v-html="conf"></code></pre>
|
||||||
</div>
|
</div>
|
||||||
|
@ -46,5 +46,10 @@ THE SOFTWARE.
|
||||||
console.info(`Highlighting ${this.$props.name}...`);
|
console.info(`Highlighting ${this.$props.name}...`);
|
||||||
Prism.highlightAllUnder(this.$el);
|
Prism.highlightAllUnder(this.$el);
|
||||||
},
|
},
|
||||||
|
methods: {
|
||||||
|
copied(event) {
|
||||||
|
this.$emit('copied', event.detail.text);
|
||||||
|
},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<!--
|
<!--
|
||||||
Copyright 2020 DigitalOcean
|
Copyright 2021 DigitalOcean
|
||||||
|
|
||||||
This code is licensed under the MIT License.
|
This code is licensed under the MIT License.
|
||||||
You may obtain a copy of the License at
|
You may obtain a copy of the License at
|
||||||
|
@ -25,7 +25,7 @@ THE SOFTWARE.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div :class="`column ${half ? 'is-half' : 'is-full'} is-full-mobile is-full-tablet`">
|
<div :class="`column ${half ? 'is-half' : 'is-full'} is-full-mobile is-full-tablet`" @copied="copied">
|
||||||
<h3 v-html="name"></h3>
|
<h3 v-html="name"></h3>
|
||||||
<pre><code class="language-nginx" v-html="conf"></code></pre>
|
<pre><code class="language-nginx" v-html="conf"></code></pre>
|
||||||
</div>
|
</div>
|
||||||
|
@ -45,5 +45,10 @@ THE SOFTWARE.
|
||||||
console.info(`Highlighting ${this.$props.name}...`);
|
console.info(`Highlighting ${this.$props.name}...`);
|
||||||
Prism.highlightAllUnder(this.$el);
|
Prism.highlightAllUnder(this.$el);
|
||||||
},
|
},
|
||||||
|
methods: {
|
||||||
|
copied(event) {
|
||||||
|
this.$emit('copied', event.detail.text);
|
||||||
|
},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<!--
|
<!--
|
||||||
Copyright 2020 DigitalOcean
|
Copyright 2021 DigitalOcean
|
||||||
|
|
||||||
This code is licensed under the MIT License.
|
This code is licensed under the MIT License.
|
||||||
You may obtain a copy of the License at
|
You may obtain a copy of the License at
|
||||||
|
@ -25,7 +25,7 @@ THE SOFTWARE.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div :class="`column ${half ? 'is-half' : 'is-full'} is-full-mobile is-full-tablet`">
|
<div :class="`column ${half ? 'is-half' : 'is-full'} is-full-mobile is-full-tablet`" @copied="copied">
|
||||||
<h3 v-html="name"></h3>
|
<h3 v-html="name"></h3>
|
||||||
<pre><code class="language-yaml" v-html="conf"></code></pre>
|
<pre><code class="language-yaml" v-html="conf"></code></pre>
|
||||||
</div>
|
</div>
|
||||||
|
@ -46,5 +46,10 @@ THE SOFTWARE.
|
||||||
console.info(`Highlighting ${this.$props.name}...`);
|
console.info(`Highlighting ${this.$props.name}...`);
|
||||||
Prism.highlightAllUnder(this.$el);
|
Prism.highlightAllUnder(this.$el);
|
||||||
},
|
},
|
||||||
|
methods: {
|
||||||
|
copied(event) {
|
||||||
|
this.$emit('copied', event.detail.text);
|
||||||
|
},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<!--
|
<!--
|
||||||
Copyright 2020 DigitalOcean
|
Copyright 2021 DigitalOcean
|
||||||
|
|
||||||
This code is licensed under the MIT License.
|
This code is licensed under the MIT License.
|
||||||
You may obtain a copy of the License at
|
You may obtain a copy of the License at
|
||||||
|
@ -30,7 +30,7 @@ THE SOFTWARE.
|
||||||
<div class="tabs">
|
<div class="tabs">
|
||||||
<ul>
|
<ul>
|
||||||
<li v-for="tab in tabs" :class="tabClass(tab.key)">
|
<li v-for="tab in tabs" :class="tabClass(tab.key)">
|
||||||
<a @click="active = tab.key">{{ $t(tab.display) }}</a>
|
<a @click="showTab(tab.key)">{{ $t(tab.display) }}</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
@ -44,10 +44,10 @@ THE SOFTWARE.
|
||||||
></component>
|
></component>
|
||||||
|
|
||||||
<div class="navigation-buttons">
|
<div class="navigation-buttons">
|
||||||
<a v-if="previousTab !== false" class="button is-mini" @click="active = previousTab">
|
<a v-if="previousTab !== false" class="button is-mini" @click="showPreviousTab">
|
||||||
<i class="fas fa-long-arrow-alt-left"></i> <span>{{ $t('common.back') }}</span>
|
<i class="fas fa-long-arrow-alt-left"></i> <span>{{ $t('common.back') }}</span>
|
||||||
</a>
|
</a>
|
||||||
<a v-if="nextTab !== false" class="button is-primary is-mini" @click="active = nextTab">
|
<a v-if="nextTab !== false" class="button is-primary is-mini" @click="showNextTab">
|
||||||
<span>{{ $t('common.next') }}</span> <i class="fas fa-long-arrow-alt-right"></i>
|
<span>{{ $t('common.next') }}</span> <i class="fas fa-long-arrow-alt-right"></i>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
@ -92,6 +92,9 @@ THE SOFTWARE.
|
||||||
if (index >= 0) return tabs[index];
|
if (index >= 0) return tabs[index];
|
||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
|
domainCount() {
|
||||||
|
return this.$props.data.domains.filter(d => d !== null).length;
|
||||||
|
},
|
||||||
tarName() {
|
tarName() {
|
||||||
const domains = this.$props.data.domains.filter(d => d !== null).map(d => d.server.domain.computed);
|
const domains = this.$props.data.domains.filter(d => d !== null).map(d => d.server.domain.computed);
|
||||||
return `nginxconfig.io-${domains.join(',')}.tar.gz`;
|
return `nginxconfig.io-${domains.join(',')}.tar.gz`;
|
||||||
|
@ -123,11 +126,27 @@ THE SOFTWARE.
|
||||||
return new Tar(data).gz();
|
return new Tar(data).gz();
|
||||||
},
|
},
|
||||||
downloadTar() {
|
downloadTar() {
|
||||||
analytics('download_tar', 'Download', this.tarName);
|
// Analytics
|
||||||
|
analytics({
|
||||||
|
category: 'Setup',
|
||||||
|
action: 'Downloaded tar file',
|
||||||
|
label: this.tarName,
|
||||||
|
value: this.domainCount,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Do tar generation
|
||||||
this.tarContents().download(this.tarName);
|
this.tarContents().download(this.tarName);
|
||||||
},
|
},
|
||||||
copyTar() {
|
copyTar() {
|
||||||
analytics('download_base64', 'Download', this.tarName);
|
// Analytics
|
||||||
|
analytics({
|
||||||
|
category: 'Setup',
|
||||||
|
action: 'Copied base64 tar',
|
||||||
|
label: this.tarName,
|
||||||
|
value: this.domainCount,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Do tar generation
|
||||||
const path = `${this.$props.data.global.nginx.nginxConfigDirectory.computed}/${this.tarName}`;
|
const path = `${this.$props.data.global.nginx.nginxConfigDirectory.computed}/${this.tarName}`;
|
||||||
return this.tarContents().base64(path);
|
return this.tarContents().base64(path);
|
||||||
},
|
},
|
||||||
|
@ -155,6 +174,39 @@ THE SOFTWARE.
|
||||||
resetText();
|
resetText();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
showTab(target) {
|
||||||
|
// Analytics
|
||||||
|
analytics({
|
||||||
|
category: 'Setup',
|
||||||
|
action: 'Tab clicked',
|
||||||
|
label: `${this.$data.active}, ${target}`,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Go!
|
||||||
|
this.$data.active = target;
|
||||||
|
},
|
||||||
|
showPreviousTab() {
|
||||||
|
// Analytics
|
||||||
|
analytics({
|
||||||
|
category: 'Setup',
|
||||||
|
action: 'Back clicked',
|
||||||
|
label: `${this.$data.active}, ${this.previousTab}`,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Go!
|
||||||
|
this.$data.active = this.previousTab;
|
||||||
|
},
|
||||||
|
showNextTab() {
|
||||||
|
// Analytics
|
||||||
|
analytics({
|
||||||
|
category: 'Setup',
|
||||||
|
action: 'Next clicked',
|
||||||
|
label: `${this.$data.active}, ${this.nextTab}`,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Go!
|
||||||
|
this.$data.active = this.nextTab;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<!--
|
<!--
|
||||||
Copyright 2020 DigitalOcean
|
Copyright 2021 DigitalOcean
|
||||||
|
|
||||||
This code is licensed under the MIT License.
|
This code is licensed under the MIT License.
|
||||||
You may obtain a copy of the License at
|
You may obtain a copy of the License at
|
||||||
|
@ -34,6 +34,7 @@ THE SOFTWARE.
|
||||||
</p>
|
</p>
|
||||||
<BashPrism :key="sitesAvailable"
|
<BashPrism :key="sitesAvailable"
|
||||||
:cmd="`sed -i -r 's/(listen .*443)/\\1;#/g; s/(ssl_(certificate|certificate_key|trusted_certificate) )/#;#\\1/g' ${sitesAvailable}`"
|
:cmd="`sed -i -r 's/(listen .*443)/\\1;#/g; s/(ssl_(certificate|certificate_key|trusted_certificate) )/#;#\\1/g' ${sitesAvailable}`"
|
||||||
|
@copied="codeCopiedEvent('Disable ssl directives')"
|
||||||
></BashPrism>
|
></BashPrism>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
|
@ -42,7 +43,9 @@ THE SOFTWARE.
|
||||||
{{ $t('templates.setupSections.certbot.reloadYourNginxServer') }}
|
{{ $t('templates.setupSections.certbot.reloadYourNginxServer') }}
|
||||||
<br />
|
<br />
|
||||||
</p>
|
</p>
|
||||||
<BashPrism cmd="sudo nginx -t && sudo systemctl reload nginx"></BashPrism>
|
<BashPrism cmd="sudo nginx -t && sudo systemctl reload nginx"
|
||||||
|
@copied="codeCopiedEvent('Reload nginx')"
|
||||||
|
></BashPrism>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li>
|
<li>
|
||||||
|
@ -50,7 +53,10 @@ THE SOFTWARE.
|
||||||
{{ $t('templates.setupSections.certbot.obtainSslCertificatesFromLetsEncrypt') }}
|
{{ $t('templates.setupSections.certbot.obtainSslCertificatesFromLetsEncrypt') }}
|
||||||
<br />
|
<br />
|
||||||
</p>
|
</p>
|
||||||
<BashPrism :key="certbotCmds" :cmd="certbotCmds"></BashPrism>
|
<BashPrism :key="certbotCmds"
|
||||||
|
:cmd="certbotCmds"
|
||||||
|
@copied="codeCopiedEvent('Obtain certificates using certbot')"
|
||||||
|
></BashPrism>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li>
|
<li>
|
||||||
|
@ -58,7 +64,10 @@ THE SOFTWARE.
|
||||||
{{ $t('templates.setupSections.certbot.uncommentSslDirectivesInConfiguration') }}
|
{{ $t('templates.setupSections.certbot.uncommentSslDirectivesInConfiguration') }}
|
||||||
<br />
|
<br />
|
||||||
</p>
|
</p>
|
||||||
<BashPrism :key="sitesAvailable" :cmd="`sed -i -r 's/#?;#//g' ${sitesAvailable}`"></BashPrism>
|
<BashPrism :key="sitesAvailable"
|
||||||
|
:cmd="`sed -i -r 's/#?;#//g' ${sitesAvailable}`"
|
||||||
|
@copied="codeCopiedEvent('Enable ssl directives')"
|
||||||
|
></BashPrism>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li>
|
<li>
|
||||||
|
@ -66,7 +75,9 @@ THE SOFTWARE.
|
||||||
{{ $t('templates.setupSections.certbot.reloadYourNginxServer') }}
|
{{ $t('templates.setupSections.certbot.reloadYourNginxServer') }}
|
||||||
<br />
|
<br />
|
||||||
</p>
|
</p>
|
||||||
<BashPrism cmd="sudo nginx -t && sudo systemctl reload nginx"></BashPrism>
|
<BashPrism cmd="sudo nginx -t && sudo systemctl reload nginx"
|
||||||
|
@copied="codeCopiedEvent('Reload nginx (2)')"
|
||||||
|
></BashPrism>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li>
|
<li>
|
||||||
|
@ -74,8 +85,12 @@ THE SOFTWARE.
|
||||||
{{ $t('templates.setupSections.certbot.configureCertbotToReloadNginxOnCertificateRenewal') }}
|
{{ $t('templates.setupSections.certbot.configureCertbotToReloadNginxOnCertificateRenewal') }}
|
||||||
<br />
|
<br />
|
||||||
</p>
|
</p>
|
||||||
<BashPrism cmd="echo -e '#!/bin/bash\nnginx -t && systemctl reload nginx' | sudo tee /etc/letsencrypt/renewal-hooks/post/nginx-reload.sh"></BashPrism>
|
<BashPrism cmd="echo -e '#!/bin/bash\nnginx -t && systemctl reload nginx' | sudo tee /etc/letsencrypt/renewal-hooks/post/nginx-reload.sh"
|
||||||
<BashPrism cmd="sudo chmod a+x /etc/letsencrypt/renewal-hooks/post/nginx-reload.sh"></BashPrism>
|
@copied="codeCopiedEvent('Create nginx auto-restart on renewal')"
|
||||||
|
></BashPrism>
|
||||||
|
<BashPrism cmd="sudo chmod a+x /etc/letsencrypt/renewal-hooks/post/nginx-reload.sh"
|
||||||
|
@copied="codeCopiedEvent('Enable execution of auto-restart')"
|
||||||
|
></BashPrism>
|
||||||
</li>
|
</li>
|
||||||
</ol>
|
</ol>
|
||||||
|
|
||||||
|
@ -95,6 +110,7 @@ THE SOFTWARE.
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import BashPrism from '../prism/bash';
|
import BashPrism from '../prism/bash';
|
||||||
|
import analytics from '../../util/analytics';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'SetupCertbot',
|
name: 'SetupCertbot',
|
||||||
|
@ -144,5 +160,14 @@ THE SOFTWARE.
|
||||||
)).join('\n');
|
)).join('\n');
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
methods: {
|
||||||
|
codeCopiedEvent(step) {
|
||||||
|
analytics({
|
||||||
|
category: 'Setup',
|
||||||
|
action: 'Code snippet copied',
|
||||||
|
label: `certbot: ${step}`,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<!--
|
<!--
|
||||||
Copyright 2020 DigitalOcean
|
Copyright 2021 DigitalOcean
|
||||||
|
|
||||||
This code is licensed under the MIT License.
|
This code is licensed under the MIT License.
|
||||||
You may obtain a copy of the License at
|
You may obtain a copy of the License at
|
||||||
|
@ -52,6 +52,7 @@ THE SOFTWARE.
|
||||||
<br />
|
<br />
|
||||||
<BashPrism :key="$props.data.global.nginx.nginxConfigDirectory.computed"
|
<BashPrism :key="$props.data.global.nginx.nginxConfigDirectory.computed"
|
||||||
:cmd="`cd ${$props.data.global.nginx.nginxConfigDirectory.computed}`"
|
:cmd="`cd ${$props.data.global.nginx.nginxConfigDirectory.computed}`"
|
||||||
|
@copied="codeCopiedEvent('Navigate to nginx config directory')"
|
||||||
></BashPrism>
|
></BashPrism>
|
||||||
</p>
|
</p>
|
||||||
</li>
|
</li>
|
||||||
|
@ -60,7 +61,9 @@ THE SOFTWARE.
|
||||||
<p>
|
<p>
|
||||||
<span v-html="$t('templates.setupSections.download.createABackupOfYourCurrentNginxConfiguration')"></span>
|
<span v-html="$t('templates.setupSections.download.createABackupOfYourCurrentNginxConfiguration')"></span>
|
||||||
<br />
|
<br />
|
||||||
<BashPrism cmd="tar -czvf nginx_$(date +'%F_%H-%M-%S').tar.gz nginx.conf sites-available/ sites-enabled/ nginxconfig.io/"></BashPrism>
|
<BashPrism cmd="tar -czvf nginx_$(date +'%F_%H-%M-%S').tar.gz nginx.conf sites-available/ sites-enabled/ nginxconfig.io/"
|
||||||
|
@copied="codeCopiedEvent('Create nginx config backup tar')"
|
||||||
|
></BashPrism>
|
||||||
</p>
|
</p>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
|
@ -68,7 +71,10 @@ THE SOFTWARE.
|
||||||
<p>
|
<p>
|
||||||
<span v-html="$t('templates.setupSections.download.extractTheNewCompressedConfigurationArchiveUsingTar')"></span>
|
<span v-html="$t('templates.setupSections.download.extractTheNewCompressedConfigurationArchiveUsingTar')"></span>
|
||||||
<br />
|
<br />
|
||||||
<BashPrism :key="$parent.tarName" :cmd="`tar -xzvf ${$parent.tarName}`"></BashPrism>
|
<BashPrism :key="$parent.tarName"
|
||||||
|
:cmd="`tar -xzvf ${$parent.tarName}`"
|
||||||
|
@copied="codeCopiedEvent('Extract new nginx config tar')"
|
||||||
|
></BashPrism>
|
||||||
</p>
|
</p>
|
||||||
</li>
|
</li>
|
||||||
</ol>
|
</ol>
|
||||||
|
@ -77,6 +83,7 @@ THE SOFTWARE.
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import BashPrism from '../prism/bash';
|
import BashPrism from '../prism/bash';
|
||||||
|
import analytics from '../../util/analytics';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'SetupDownload',
|
name: 'SetupDownload',
|
||||||
|
@ -91,5 +98,14 @@ THE SOFTWARE.
|
||||||
mounted() {
|
mounted() {
|
||||||
this.$parent.setupCopy(this.$refs.copyTar);
|
this.$parent.setupCopy(this.$refs.copyTar);
|
||||||
},
|
},
|
||||||
|
methods: {
|
||||||
|
codeCopiedEvent(step) {
|
||||||
|
analytics({
|
||||||
|
category: 'Setup',
|
||||||
|
action: 'Code snippet copied',
|
||||||
|
label: `download: ${step}`,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<!--
|
<!--
|
||||||
Copyright 2020 DigitalOcean
|
Copyright 2021 DigitalOcean
|
||||||
|
|
||||||
This code is licensed under the MIT License.
|
This code is licensed under the MIT License.
|
||||||
You may obtain a copy of the License at
|
You may obtain a copy of the License at
|
||||||
|
@ -32,13 +32,16 @@ THE SOFTWARE.
|
||||||
<p>
|
<p>
|
||||||
{{ $t('templates.setupSections.goLive.reloadNginxToLoadInYourNewConfiguration') }}
|
{{ $t('templates.setupSections.goLive.reloadNginxToLoadInYourNewConfiguration') }}
|
||||||
<br />
|
<br />
|
||||||
<BashPrism cmd="sudo nginx -t && sudo systemctl reload nginx"></BashPrism>
|
<BashPrism cmd="sudo nginx -t && sudo systemctl reload nginx"
|
||||||
|
@copied="codeCopiedEvent('Reload nginx')"
|
||||||
|
></BashPrism>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import BashPrism from '../prism/bash';
|
import BashPrism from '../prism/bash';
|
||||||
|
import analytics from '../../util/analytics';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'SetupGoLive',
|
name: 'SetupGoLive',
|
||||||
|
@ -50,5 +53,14 @@ THE SOFTWARE.
|
||||||
props: {
|
props: {
|
||||||
data: Object,
|
data: Object,
|
||||||
},
|
},
|
||||||
|
methods: {
|
||||||
|
codeCopiedEvent(step) {
|
||||||
|
analytics({
|
||||||
|
category: 'Setup',
|
||||||
|
action: 'Code snippet copied',
|
||||||
|
label: `goLive: ${step}`,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<!--
|
<!--
|
||||||
Copyright 2020 DigitalOcean
|
Copyright 2021 DigitalOcean
|
||||||
|
|
||||||
This code is licensed under the MIT License.
|
This code is licensed under the MIT License.
|
||||||
You may obtain a copy of the License at
|
You may obtain a copy of the License at
|
||||||
|
@ -33,6 +33,7 @@ THE SOFTWARE.
|
||||||
<br />
|
<br />
|
||||||
<BashPrism :key="`${$props.data.global.nginx.nginxConfigDirectory.computed}-${diffieHellmanValue}`"
|
<BashPrism :key="`${$props.data.global.nginx.nginxConfigDirectory.computed}-${diffieHellmanValue}`"
|
||||||
:cmd="`openssl dhparam -out ${$props.data.global.nginx.nginxConfigDirectory.computed}/dhparam.pem ${diffieHellmanValue}`"
|
:cmd="`openssl dhparam -out ${$props.data.global.nginx.nginxConfigDirectory.computed}/dhparam.pem ${diffieHellmanValue}`"
|
||||||
|
@copied="codeCopiedEvent('Generate diffie-hellman keys')"
|
||||||
></BashPrism>
|
></BashPrism>
|
||||||
</p>
|
</p>
|
||||||
</li>
|
</li>
|
||||||
|
@ -41,9 +42,13 @@ THE SOFTWARE.
|
||||||
<p>
|
<p>
|
||||||
<span v-html="$t('templates.setupSections.ssl.createACommonAcmeChallengeDirectoryForLetsEncrypt')"></span>
|
<span v-html="$t('templates.setupSections.ssl.createACommonAcmeChallengeDirectoryForLetsEncrypt')"></span>
|
||||||
<br />
|
<br />
|
||||||
<BashPrism :key="letsEncryptDir" :cmd="`mkdir -p ${letsEncryptDir}`"></BashPrism>
|
<BashPrism :key="letsEncryptDir"
|
||||||
|
:cmd="`mkdir -p ${letsEncryptDir}`"
|
||||||
|
@copied="codeCopiedEvent('Create let\'s encrypt directory')"
|
||||||
|
></BashPrism>
|
||||||
<BashPrism :key="`${nginxUser}-${letsEncryptDir}`"
|
<BashPrism :key="`${nginxUser}-${letsEncryptDir}`"
|
||||||
:cmd="`chown ${nginxUser} ${letsEncryptDir}`"
|
:cmd="`chown ${nginxUser} ${letsEncryptDir}`"
|
||||||
|
@copied="codeCopiedEvent('Set let\'s encrypt directory ownership')"
|
||||||
></BashPrism>
|
></BashPrism>
|
||||||
</p>
|
</p>
|
||||||
</li>
|
</li>
|
||||||
|
@ -65,6 +70,7 @@ THE SOFTWARE.
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import BashPrism from '../prism/bash';
|
import BashPrism from '../prism/bash';
|
||||||
|
import analytics from '../../util/analytics';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'SetupSSL',
|
name: 'SetupSSL',
|
||||||
|
@ -103,5 +109,14 @@ THE SOFTWARE.
|
||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
methods: {
|
||||||
|
codeCopiedEvent(step) {
|
||||||
|
analytics({
|
||||||
|
category: 'Setup',
|
||||||
|
action: 'Code snippet copied',
|
||||||
|
label: `ssl: ${step}`,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
Copyright 2020 DigitalOcean
|
Copyright 2021 DigitalOcean
|
||||||
|
|
||||||
This code is licensed under the MIT License.
|
This code is licensed under the MIT License.
|
||||||
You may obtain a copy of the License at
|
You may obtain a copy of the License at
|
||||||
|
@ -24,49 +24,234 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
THE SOFTWARE.
|
THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export default (action, category, label, value, nonInteraction = false) => {
|
export default ({ category, action, label, value, nonInteraction }) => {
|
||||||
|
console.info('Analytics event:', { category, action, label, value, nonInteraction });
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const tracker = window.ga.getAll()[0];
|
// Google
|
||||||
if (tracker) {
|
window.ga('send', 'event', {
|
||||||
tracker.send({
|
eventCategory: category,
|
||||||
hitType: 'event',
|
eventAction: action,
|
||||||
eventCategory: category,
|
eventLabel: label,
|
||||||
eventAction: action,
|
eventValue: value,
|
||||||
eventLabel: label,
|
nonInteraction,
|
||||||
eventValue: value,
|
});
|
||||||
nonInteraction,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} catch (_) {
|
} catch (_) {
|
||||||
// If analytics fail, don't block anything else
|
// If analytics fail, don't block anything else
|
||||||
}
|
}
|
||||||
|
|
||||||
/*try {
|
try {
|
||||||
// gtag.js
|
// Segment
|
||||||
if (window.gtag) {
|
window.analytics.track(`${category} ${action}`, {
|
||||||
window.gtag('event', action, {
|
label,
|
||||||
event_category: category,
|
value,
|
||||||
event_label: label,
|
nonInteraction,
|
||||||
value,
|
});
|
||||||
});
|
|
||||||
}
|
|
||||||
} catch (_) {
|
} catch (_) {
|
||||||
// If analytics fail, don't block anything else
|
// If analytics fail, don't block anything else
|
||||||
}*/
|
}
|
||||||
|
|
||||||
/*try {
|
|
||||||
// analytics.js
|
|
||||||
if (window.ga) {
|
|
||||||
window.ga('send', {
|
|
||||||
hitType: 'event',
|
|
||||||
eventCategory: category,
|
|
||||||
eventAction: action,
|
|
||||||
eventLabel: label,
|
|
||||||
eventValue: value,
|
|
||||||
nonInteraction,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} catch (_) {
|
|
||||||
// If analytics fail, don't block anything else
|
|
||||||
}*/
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
All analytics events in app:
|
||||||
|
|
||||||
|
# Initial language set (from browser or query param)
|
||||||
|
|
||||||
|
File: app.vue
|
||||||
|
Category: 'Language'
|
||||||
|
Action: 'Set'
|
||||||
|
Label: language pack name
|
||||||
|
Non-interaction: true
|
||||||
|
|
||||||
|
# User manually changing tool language
|
||||||
|
|
||||||
|
File: app.vue
|
||||||
|
Category: 'Language'
|
||||||
|
Action: 'Set'
|
||||||
|
Label: language pack name
|
||||||
|
Non-interaction: false
|
||||||
|
|
||||||
|
# Initial domains set (from query params)
|
||||||
|
|
||||||
|
File: app.vue
|
||||||
|
Category: 'Site'
|
||||||
|
Action: 'Added'
|
||||||
|
Value: total number of sites active in tool
|
||||||
|
Non-interaction: true
|
||||||
|
|
||||||
|
# User adding a domain
|
||||||
|
|
||||||
|
File: app.vue
|
||||||
|
Category: 'Site'
|
||||||
|
Action: 'Added'
|
||||||
|
Value: total number of sites active in tool
|
||||||
|
Non-interaction: false
|
||||||
|
|
||||||
|
# User removing a domain
|
||||||
|
|
||||||
|
File: app.vue
|
||||||
|
Category: 'Site'
|
||||||
|
Action: 'Removed'
|
||||||
|
Label: domain name being removed
|
||||||
|
Value: total number of sites active in tool after removal
|
||||||
|
|
||||||
|
# Initial split column mode (will always be disabled)
|
||||||
|
|
||||||
|
File: app.vue
|
||||||
|
Category: 'Split column'
|
||||||
|
Action: 'Disabled'
|
||||||
|
Non-interaction: true
|
||||||
|
|
||||||
|
# User changing the column mode
|
||||||
|
|
||||||
|
File: app.vue
|
||||||
|
Category: 'Split column'
|
||||||
|
Action: 'Disabled' / 'Enabled'
|
||||||
|
Non-interaction: false
|
||||||
|
|
||||||
|
# User applying a preset
|
||||||
|
|
||||||
|
File: domain_sections/presets.vue
|
||||||
|
Category: 'Preset'
|
||||||
|
Action: 'Applied'
|
||||||
|
Label: preset internal name
|
||||||
|
|
||||||
|
# User applying a preset with previous customisations
|
||||||
|
|
||||||
|
File: domain_sections/presets.vue
|
||||||
|
Category: 'Preset'
|
||||||
|
Action: 'Overwritten'
|
||||||
|
Label: preset internal name
|
||||||
|
|
||||||
|
# User resetting global settings
|
||||||
|
|
||||||
|
File: global_sections/tools.vue
|
||||||
|
Category: 'Tools'
|
||||||
|
Action: 'Global settings reset'
|
||||||
|
|
||||||
|
# User resetting a domain
|
||||||
|
|
||||||
|
File: global_sections/tools.vue
|
||||||
|
Category: 'Tools'
|
||||||
|
Action: 'Site reset'
|
||||||
|
Label: domain name being reset
|
||||||
|
|
||||||
|
# User removing a domain in the tools tab
|
||||||
|
|
||||||
|
Note: This will also trigger the regular site removal event in app.vue
|
||||||
|
File: global_sections/tools.vue
|
||||||
|
Category: 'Tools'
|
||||||
|
Action: 'Removed site'
|
||||||
|
Label: domain name being removed
|
||||||
|
|
||||||
|
# User resetting all domains
|
||||||
|
|
||||||
|
File: global_sections/tools.vue
|
||||||
|
Category: 'Tools'
|
||||||
|
Action: 'All sites reset'
|
||||||
|
Label: comma-separated list of domain names being reset
|
||||||
|
Value: total number of domains being reset
|
||||||
|
|
||||||
|
# User removing all domains
|
||||||
|
|
||||||
|
Note: This will also trigger the regular site removal event in app.vue for each domain removed
|
||||||
|
File: global_sections/tools.vue
|
||||||
|
Category: 'Tools'
|
||||||
|
Action: 'All sites removed'
|
||||||
|
Label: comma-separated list of domain names being removed
|
||||||
|
Value: total number of domains being removed
|
||||||
|
|
||||||
|
# User clicking a tab in global settings
|
||||||
|
|
||||||
|
File: global.vue
|
||||||
|
Category: 'Global'
|
||||||
|
Action: 'Tab clicked'
|
||||||
|
Label: from tab, to tab
|
||||||
|
|
||||||
|
# User clicking back in global settings
|
||||||
|
|
||||||
|
File: global.vue
|
||||||
|
Category: 'Global'
|
||||||
|
Action: 'Back clicked'
|
||||||
|
Label: from tab, to tab
|
||||||
|
|
||||||
|
# User clicking next in global settings
|
||||||
|
|
||||||
|
File: global.vue
|
||||||
|
Category: 'Global'
|
||||||
|
Action: 'Next clicked'
|
||||||
|
Label: from tab, to tab
|
||||||
|
|
||||||
|
# User clicking a tab in domain settings
|
||||||
|
|
||||||
|
File: domain.vue
|
||||||
|
Category: 'Site'
|
||||||
|
Action: 'Tab clicked'
|
||||||
|
Label: from tab, to tab
|
||||||
|
|
||||||
|
# User clicking back in domain settings
|
||||||
|
|
||||||
|
File: domain.vue
|
||||||
|
Category: 'Site'
|
||||||
|
Action: 'Back clicked'
|
||||||
|
Label: from tab, to tab
|
||||||
|
|
||||||
|
# User clicking next in domain settings
|
||||||
|
|
||||||
|
File: domain.vue
|
||||||
|
Category: 'Site'
|
||||||
|
Action: 'Next clicked'
|
||||||
|
Label: from tab, to tab
|
||||||
|
|
||||||
|
# User clicking a tab in setup
|
||||||
|
|
||||||
|
File: setup.vue
|
||||||
|
Category: 'Setup'
|
||||||
|
Action: 'Tab clicked'
|
||||||
|
Label: from tab, to tab
|
||||||
|
|
||||||
|
# User clicking back in setup
|
||||||
|
|
||||||
|
File: setup.vue
|
||||||
|
Category: 'Setup'
|
||||||
|
Action: 'Back clicked'
|
||||||
|
Label: from tab, to tab
|
||||||
|
|
||||||
|
# User clicking next in setup
|
||||||
|
|
||||||
|
File: setup.vue
|
||||||
|
Category: 'Setup'
|
||||||
|
Action: 'Next clicked'
|
||||||
|
Label: from tab, to tab
|
||||||
|
|
||||||
|
# User downloading the config
|
||||||
|
|
||||||
|
File: setup.vue
|
||||||
|
Category: 'Setup'
|
||||||
|
Action: 'Downloaded tar file'
|
||||||
|
Label: name of the tar file (incl. domain names)
|
||||||
|
Value: total number of active domains
|
||||||
|
|
||||||
|
# User copying the base64 config
|
||||||
|
|
||||||
|
File: setup.vue
|
||||||
|
Category: 'Setup'
|
||||||
|
Action: 'Copied base64 tar'
|
||||||
|
Label: name of the tar file (incl. domain names)
|
||||||
|
Value: total number of active domains
|
||||||
|
|
||||||
|
# User copying a code snippet in setup
|
||||||
|
|
||||||
|
File: setup.vue
|
||||||
|
Category: 'Setup'
|
||||||
|
Action: 'Code snippet copied'
|
||||||
|
Label: tab name: a summary of the code snippet
|
||||||
|
|
||||||
|
# User copying a config file
|
||||||
|
|
||||||
|
File: app.vue
|
||||||
|
Category: 'Config files'
|
||||||
|
Action: 'Code snippet copied'
|
||||||
|
Label: name of file without nginx directory
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
Copyright 2020 DigitalOcean
|
Copyright 2021 DigitalOcean
|
||||||
|
|
||||||
This code is licensed under the MIT License.
|
This code is licensed under the MIT License.
|
||||||
You may obtain a copy of the License at
|
You may obtain a copy of the License at
|
||||||
|
@ -24,10 +24,57 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
THE SOFTWARE.
|
THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import 'prismjs';
|
import Clipboard from 'clipboard';
|
||||||
|
import Prism from 'prismjs';
|
||||||
import 'prismjs/components/prism-nginx';
|
import 'prismjs/components/prism-nginx';
|
||||||
import 'prismjs/components/prism-bash';
|
import 'prismjs/components/prism-bash';
|
||||||
import 'prismjs/plugins/keep-markup/prism-keep-markup';
|
import 'prismjs/plugins/keep-markup/prism-keep-markup';
|
||||||
import 'prismjs/plugins/toolbar/prism-toolbar';
|
import 'prismjs/plugins/toolbar/prism-toolbar';
|
||||||
import 'prismjs/plugins/toolbar/prism-toolbar.css';
|
import 'prismjs/plugins/toolbar/prism-toolbar.css';
|
||||||
import 'prismjs/plugins/copy-to-clipboard/prism-copy-to-clipboard';
|
|
||||||
|
// Custom copy to clipboard (based on the Prism one)
|
||||||
|
const copyToClipboard = () => {
|
||||||
|
if (!Prism.plugins.toolbar) {
|
||||||
|
console.warn('Copy to Clipboard loaded before Toolbar.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Prism.plugins.toolbar.registerButton('copy-to-clipboard', env => {
|
||||||
|
const linkCopy = document.createElement('button');
|
||||||
|
linkCopy.textContent = 'Copy';
|
||||||
|
|
||||||
|
const element = env.element;
|
||||||
|
const clip = new Clipboard(linkCopy, {
|
||||||
|
'text': () => element.textContent,
|
||||||
|
});
|
||||||
|
|
||||||
|
const resetText = () => {
|
||||||
|
setTimeout(() => {
|
||||||
|
linkCopy.textContent = 'Copy';
|
||||||
|
}, 5000);
|
||||||
|
};
|
||||||
|
|
||||||
|
const emitEvent = () => {
|
||||||
|
linkCopy.dispatchEvent(new CustomEvent('copied', {
|
||||||
|
bubbles: true,
|
||||||
|
detail: { text: element.textContent },
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
|
clip.on('success', () => {
|
||||||
|
linkCopy.textContent = 'Copied!';
|
||||||
|
emitEvent();
|
||||||
|
resetText();
|
||||||
|
});
|
||||||
|
|
||||||
|
clip.on('error', () => {
|
||||||
|
const isMac = navigator.platform.includes('Mac');
|
||||||
|
linkCopy.textContent = `Press ${isMac ? 'Cmd' : 'Ctrl'}+C to copy`;
|
||||||
|
resetText();
|
||||||
|
});
|
||||||
|
|
||||||
|
return linkCopy;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
copyToClipboard();
|
||||||
|
|
Loading…
Reference in New Issue