2025-03-10 21:06:33 +01:00
|
|
|
<script setup>
|
|
|
|
|
|
|
|
|
|
import { useI18n } from 'vue-i18n';
|
|
|
|
|
const i18n = useI18n();
|
|
|
|
|
const t = i18n.t;
|
|
|
|
|
|
|
|
|
|
import { ref, onMounted, useTemplateRef } from 'vue';
|
2025-03-12 13:16:33 +01:00
|
|
|
import { Button, Checkbox, InputDialog, Dialog, FormGroup, Switch } from 'pankow';
|
2025-03-10 21:06:33 +01:00
|
|
|
import Section from '../components/Section.vue';
|
|
|
|
|
import SettingsItem from '../components/SettingsItem.vue';
|
2025-03-11 12:38:54 +01:00
|
|
|
import CatchAllSettingsItem from '../components/CatchAllSettingsItem.vue';
|
2025-03-12 12:49:06 +01:00
|
|
|
import MailDomainStatus from '../components/MailDomainStatus.vue';
|
2025-03-10 21:06:33 +01:00
|
|
|
import DomainsModel from '../models/DomainsModel.js';
|
|
|
|
|
import MailModel from '../models/MailModel.js';
|
|
|
|
|
|
|
|
|
|
const domainsModel = DomainsModel.create();
|
|
|
|
|
const mailModel = MailModel.create();
|
|
|
|
|
|
|
|
|
|
const inputDialog = useTemplateRef('inputDialog');
|
|
|
|
|
const enableIncomingDialog = useTemplateRef('enableIncomingDialog');
|
|
|
|
|
|
|
|
|
|
const domain = ref('');
|
|
|
|
|
const domainProvider = ref('');
|
|
|
|
|
const mailConfig = ref({});
|
|
|
|
|
const refreshBusy = ref(false);
|
|
|
|
|
const incomingEnabled = ref(false);
|
|
|
|
|
const enableIncomeBusy = ref(false);
|
|
|
|
|
const enableIncomingSetupDns = ref(false);
|
|
|
|
|
|
|
|
|
|
async function onAskIncomingToggle(value) {
|
|
|
|
|
if (value) {
|
|
|
|
|
enableIncomeBusy.value = false;
|
|
|
|
|
return enableIncomingDialog.value.open();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const yes = await inputDialog.value.confirm({
|
|
|
|
|
title: t('email.disableEmailDialog.title', { domain: domain.value }),
|
|
|
|
|
message: t('email.disableEmailDialog.description', { domain: domain.value }),
|
|
|
|
|
confirmStyle: 'danger',
|
|
|
|
|
confirmLabel: t('email.disableEmailDialog.disableAction'),
|
|
|
|
|
rejectLabel: t('main.dialog.cancel'),
|
|
|
|
|
rejectStyle: 'secondary',
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// reset switch
|
|
|
|
|
if (!yes) return incomingEnabled.value = true;
|
|
|
|
|
|
|
|
|
|
const [error] = await mailModel.setEnabled(domain.value, false);
|
|
|
|
|
if (error) return console.error(error);
|
|
|
|
|
|
2025-03-12 12:49:06 +01:00
|
|
|
await refreshMailConfig();
|
2025-03-10 21:06:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function onEnableIncoming() {
|
|
|
|
|
enableIncomeBusy.value = true;
|
|
|
|
|
|
|
|
|
|
const [error] = await mailModel.setEnabled(domain.value, true);
|
|
|
|
|
if (error) return console.error(error);
|
|
|
|
|
|
|
|
|
|
// TODO this has to be done in the backend here! reconfigureEmailApps();
|
|
|
|
|
|
|
|
|
|
if (enableIncomingSetupDns.value) {
|
|
|
|
|
const [error] = await domainsModel.setDnsRecords({ domain: domain.value, type: 'mail' });
|
|
|
|
|
if (error) console.error(error);
|
|
|
|
|
}
|
|
|
|
|
|
2025-03-12 12:49:06 +01:00
|
|
|
await refreshMailConfig();
|
|
|
|
|
|
|
|
|
|
enableIncomingDialog.value.close();
|
|
|
|
|
enableIncomeBusy.value = false;
|
2025-03-10 21:06:33 +01:00
|
|
|
}
|
|
|
|
|
|
2025-03-10 21:18:32 +01:00
|
|
|
|
|
|
|
|
const mailFromValidation = ref(false);
|
2025-03-11 12:50:05 +01:00
|
|
|
const mailFromValidationBusy = ref(false);
|
2025-03-10 21:18:32 +01:00
|
|
|
|
2025-03-11 12:50:05 +01:00
|
|
|
async function onToggleMailFromValidation(value) {
|
|
|
|
|
mailFromValidationBusy.value = true;
|
|
|
|
|
|
|
|
|
|
const [error] = await mailModel.setMailFromValidation(domain.value, value);
|
|
|
|
|
if (error) {
|
|
|
|
|
mailFromValidation.value = !value;
|
|
|
|
|
mailFromValidationBusy.value = false;
|
|
|
|
|
return console.error(error);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
mailFromValidationBusy.value = false;
|
|
|
|
|
}
|
2025-03-10 21:18:32 +01:00
|
|
|
|
2025-03-11 13:13:02 +01:00
|
|
|
|
|
|
|
|
const signatureDialog = useTemplateRef('signatureDialog');
|
|
|
|
|
const signatureBusy = ref(false);
|
|
|
|
|
const signatureText = ref('');
|
|
|
|
|
const signatureHtml = ref('');
|
|
|
|
|
|
|
|
|
|
function onShowSignatureDialog() {
|
|
|
|
|
signatureBusy.value = false;
|
|
|
|
|
signatureDialog.value.open();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function onSubmitSignature() {
|
|
|
|
|
signatureBusy.value = true;
|
|
|
|
|
|
|
|
|
|
const [error] = await mailModel.setMailBanner(domain.value, signatureText.value, signatureHtml.value);
|
|
|
|
|
if (error) return console.error(error);
|
|
|
|
|
|
|
|
|
|
signatureDialog.value.close();
|
|
|
|
|
signatureBusy.value = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2025-03-10 21:06:33 +01:00
|
|
|
async function refreshMailConfig() {
|
2025-03-12 12:49:06 +01:00
|
|
|
refreshBusy.value = true;
|
2025-03-10 21:06:33 +01:00
|
|
|
|
|
|
|
|
const [error, result] = await mailModel.config(domain.value);
|
|
|
|
|
if (error) return console.error(error);
|
|
|
|
|
|
|
|
|
|
mailConfig.value = result;
|
|
|
|
|
incomingEnabled.value = result.enabled;
|
2025-03-10 21:18:32 +01:00
|
|
|
mailFromValidation.value = result.mailFromValidation;
|
2025-03-11 13:13:02 +01:00
|
|
|
signatureText.value = mailConfig.value.banner.text || '';
|
|
|
|
|
signatureHtml.value = mailConfig.value.banner.html || '';
|
2025-03-10 21:06:33 +01:00
|
|
|
|
|
|
|
|
refreshBusy.value = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
onMounted(async () => {
|
|
|
|
|
domain.value = window.location.hash.slice('#/email/'.length);
|
|
|
|
|
if (!domain.value) return;
|
|
|
|
|
|
|
|
|
|
const [error, result] = await domainsModel.get(domain.value);
|
|
|
|
|
if (error) return console.error(error);
|
|
|
|
|
|
|
|
|
|
domainProvider.value = result.provider;
|
|
|
|
|
|
|
|
|
|
await refreshMailConfig();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<template>
|
|
|
|
|
<div>
|
|
|
|
|
<InputDialog ref="inputDialog"/>
|
2025-03-11 13:13:02 +01:00
|
|
|
|
2025-03-10 21:06:33 +01:00
|
|
|
<Dialog ref="enableIncomingDialog"
|
|
|
|
|
:title="$t('email.enableEmailDialog.title', { domain: domain })"
|
|
|
|
|
:confirm-label="$t('email.enableEmailDialog.enableAction')"
|
|
|
|
|
:confirm-busy="enableIncomeBusy"
|
|
|
|
|
:reject-label="enableIncomeBusy ? '' : $t('main.dialog.cancel')"
|
|
|
|
|
reject-style="secondary"
|
|
|
|
|
@confirm="onEnableIncoming()"
|
2025-03-11 12:38:54 +01:00
|
|
|
@reject="incomingEnabled = false"
|
2025-03-10 21:06:33 +01:00
|
|
|
>
|
|
|
|
|
<div>
|
|
|
|
|
<p v-html="$t('email.enableEmailDialog.description', { domain: domain, requiredPortsDocsLink: 'https://docs.cloudron.io/email/#required-ports' })"></p>
|
|
|
|
|
<!-- TODO <p class="text-danger" v-if="adminDomain.provider === 'cloudflare'" v-html="$t('email.enableEmailDialog.cloudflareInfo', { adminDomain: config.adminDomain, mailFqdn: config.mailFqdn })"></p> -->
|
|
|
|
|
<div v-if="domainProvider === 'noop' || domainProvider === 'manual'" v-html="$t('email.enableEmailDialog.noProviderInfo')"></div>
|
|
|
|
|
<div v-else>
|
|
|
|
|
<Checkbox v-model="enableIncomingSetupDns" :label="$t('email.enableEmailDialog.setupDnsCheckbox')"/>
|
|
|
|
|
<div v-html="$t('email.enableEmailDialog.setupDnsInfo', { importEmailDocsLink: 'https://docs.cloudron.io/guides/import-email' })"></div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</Dialog>
|
|
|
|
|
|
2025-03-11 13:13:02 +01:00
|
|
|
<Dialog ref="signatureDialog"
|
|
|
|
|
:title="$t('email.signature.title')"
|
|
|
|
|
:confirm-label="$t('main.dialog.save')"
|
|
|
|
|
:confirm-busy="signatureBusy"
|
|
|
|
|
:reject-label="signatureBusy ? '' : $t('main.dialog.cancel')"
|
|
|
|
|
reject-style="secondary"
|
|
|
|
|
@confirm="onSubmitSignature()"
|
|
|
|
|
>
|
|
|
|
|
<div>
|
|
|
|
|
<form @submit.prevent="onSubmitSignature()" autocomplete="off">
|
|
|
|
|
<fieldset :disabled="signatureBusy">
|
|
|
|
|
<input type="submit" style="display: none"/>
|
|
|
|
|
|
|
|
|
|
<FormGroup>
|
|
|
|
|
<label for="sinatureTextInput">{{ $t('email.signature.plainTextFormat') }}</label>
|
|
|
|
|
<textarea id="sinatureTextInput" v-model="signatureText" rows="4"></textarea>
|
|
|
|
|
</FormGroup>
|
|
|
|
|
|
|
|
|
|
<FormGroup>
|
|
|
|
|
<label class="sinatureHtmlInput">{{ $t('email.signature.htmlFormat') }}</label>
|
|
|
|
|
<textarea id="sinatureHtmlInput" v-model="signatureHtml" rows="4"></textarea>
|
|
|
|
|
</FormGroup>
|
|
|
|
|
|
|
|
|
|
</fieldset>
|
|
|
|
|
</form>
|
|
|
|
|
</div>
|
|
|
|
|
</Dialog>
|
|
|
|
|
|
2025-03-12 13:16:33 +01:00
|
|
|
<h1>{{ $t('email.config.title', { domain: domain }) }}</h1>
|
2025-03-10 21:06:33 +01:00
|
|
|
|
2025-03-12 12:49:06 +01:00
|
|
|
<!-- v-if here ensures the prop is already set when passed down to MailDomainStatus -->
|
|
|
|
|
<MailDomainStatus v-if="domain" :domain="domain"/>
|
|
|
|
|
|
2025-03-10 21:06:33 +01:00
|
|
|
<Section :title="$t('emails.settings.title')">
|
|
|
|
|
<SettingsItem>
|
|
|
|
|
<FormGroup>
|
|
|
|
|
<label>{{ $t('email.incoming.title') }}</label>
|
|
|
|
|
<div>{{ $t(mailConfig.enabled ? 'email.incoming.enabled' : 'email.incoming.disabled') }}</div>
|
|
|
|
|
</FormGroup>
|
|
|
|
|
<Switch v-model="incomingEnabled" :disabled="refreshBusy" @change="onAskIncomingToggle"/>
|
|
|
|
|
</SettingsItem>
|
2025-03-10 21:18:32 +01:00
|
|
|
|
|
|
|
|
<SettingsItem>
|
|
|
|
|
<FormGroup>
|
|
|
|
|
<label>{{ $t('email.masquerading.title') }}</label>
|
|
|
|
|
<div v-html="$t('email.masquerading.description')"></div>
|
|
|
|
|
</FormGroup>
|
2025-03-11 12:50:05 +01:00
|
|
|
<Switch v-model="mailFromValidation" @change="onToggleMailFromValidation" :disabled="mailFromValidationBusy"/>
|
2025-03-10 21:18:32 +01:00
|
|
|
</SettingsItem>
|
2025-03-11 12:38:54 +01:00
|
|
|
|
|
|
|
|
<CatchAllSettingsItem :domain-config="mailConfig" />
|
2025-03-11 13:13:02 +01:00
|
|
|
|
|
|
|
|
<SettingsItem>
|
|
|
|
|
<FormGroup>
|
|
|
|
|
<label>{{ $t('email.signature.title') }}</label>
|
|
|
|
|
<div v-html="$t('email.signature.description')"></div>
|
|
|
|
|
</FormGroup>
|
|
|
|
|
<div style="display: flex; align-items: center">
|
|
|
|
|
<!-- TODO translate -->
|
|
|
|
|
<Button tool plain @click="onShowSignatureDialog()">Edit</Button>
|
|
|
|
|
</div>
|
|
|
|
|
</SettingsItem>
|
2025-03-10 21:06:33 +01:00
|
|
|
</Section>
|
|
|
|
|
</div>
|
|
|
|
|
</template>
|