|
@@ -0,0 +1,367 @@
|
|
|
+// Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/
|
|
|
+
|
|
|
+import type { SetNonNullable, SetOptional } from 'type-fest'
|
|
|
+import type { Ref } from 'vue'
|
|
|
+import { computed, ref, watch } from 'vue'
|
|
|
+
|
|
|
+import MutationHandler from '#shared/server/apollo/handler/MutationHandler.ts'
|
|
|
+import { useDebouncedLoading } from '#shared/composables/useDebouncedLoading.ts'
|
|
|
+import type { FormSubmitData } from '#shared/components/Form/types.ts'
|
|
|
+import type { MutationSendError } from '#shared/types/error.ts'
|
|
|
+import type {
|
|
|
+ ChannelEmailInboundConfiguration,
|
|
|
+ ChannelEmailOutboundConfiguration,
|
|
|
+} from '#shared/graphql/types.ts'
|
|
|
+import UserError from '#shared/errors/UserError.ts'
|
|
|
+import { i18n } from '#shared/i18n.ts'
|
|
|
+
|
|
|
+import { useChannelEmailValidateConfigurationRoundtripMutation } from '#desktop/entities/channel-email/graphql/mutations/channelEmailValidateConfigurationRoundtrip.api.ts'
|
|
|
+import { useChannelEmailAddMutation } from '#desktop/entities/channel-email/graphql/mutations/channelEmailAdd.api.ts'
|
|
|
+
|
|
|
+import type {
|
|
|
+ EmailChannelSteps,
|
|
|
+ EmailChannelForms,
|
|
|
+} from '../types/email-channel.ts'
|
|
|
+import type { EmailAccountData } from '../types/email-account.ts'
|
|
|
+import type {
|
|
|
+ UpdateMetaInformationInboundFunction,
|
|
|
+ EmailInboundMetaInformation,
|
|
|
+ EmailOutboundData,
|
|
|
+ EmailInboundData,
|
|
|
+ EmailInboundMessagesData,
|
|
|
+} from '../types/email-inbound-outbound.ts'
|
|
|
+import { useChannelEmailGuessConfigurationMutation } from '../graphql/mutations/channelEmailGuessConfiguration.api.ts'
|
|
|
+import { useChannelEmailValidateConfigurationInboundMutation } from '../graphql/mutations/channelEmailValidateConfigurationInbound.api.ts'
|
|
|
+import { useChannelEmailValidateConfigurationOutboundMutation } from '../graphql/mutations/channelEmailValidateConfigurationOutbound.api.ts'
|
|
|
+
|
|
|
+export const useEmailChannelConfiguration = (
|
|
|
+ emailChannelForms: EmailChannelForms,
|
|
|
+ metaInformationInbound: Ref<Maybe<EmailInboundMetaInformation>>,
|
|
|
+ updateMetaInformationInbound: UpdateMetaInformationInboundFunction,
|
|
|
+ onSuccessCallback: () => void,
|
|
|
+) => {
|
|
|
+ const { loading, debouncedLoading } = useDebouncedLoading()
|
|
|
+ const activeStep = ref<EmailChannelSteps>('account')
|
|
|
+ const pendingActiveStep = ref<Maybe<EmailChannelSteps>>(null)
|
|
|
+
|
|
|
+ const setActiveStep = (nextStep: EmailChannelSteps) => {
|
|
|
+ if (!debouncedLoading.value) {
|
|
|
+ activeStep.value = nextStep
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ console.log('pendingActiveStep', nextStep)
|
|
|
+ pendingActiveStep.value = nextStep
|
|
|
+ }
|
|
|
+
|
|
|
+ watch(debouncedLoading, (newValue: boolean) => {
|
|
|
+ console.log('activeStep', activeStep.value)
|
|
|
+ if (!newValue && pendingActiveStep.value) {
|
|
|
+ console.log('SWITCH', pendingActiveStep.value)
|
|
|
+ activeStep.value = pendingActiveStep.value
|
|
|
+ pendingActiveStep.value = null
|
|
|
+ }
|
|
|
+ })
|
|
|
+
|
|
|
+ const stepTitle = computed(() => {
|
|
|
+ switch (activeStep.value) {
|
|
|
+ case 'inbound':
|
|
|
+ case 'inbound-messages':
|
|
|
+ return __('Email Inbound')
|
|
|
+ case 'outbound':
|
|
|
+ return __('Email Outbound')
|
|
|
+ default:
|
|
|
+ return __('Email Account')
|
|
|
+ }
|
|
|
+ })
|
|
|
+
|
|
|
+ const activeForm = computed(() => {
|
|
|
+ switch (activeStep.value) {
|
|
|
+ case 'inbound':
|
|
|
+ return emailChannelForms.emailInbound.form.value
|
|
|
+ case 'inbound-messages':
|
|
|
+ return emailChannelForms.emailInboundMessages.form.value
|
|
|
+ case 'outbound':
|
|
|
+ return emailChannelForms.emailOutbound.form.value
|
|
|
+ default:
|
|
|
+ return emailChannelForms.emailAccount.form.value
|
|
|
+ }
|
|
|
+ })
|
|
|
+
|
|
|
+ const validateConfigurationRoundtripAndChannelAdd = async (
|
|
|
+ account: EmailAccountData,
|
|
|
+ inboundConfiguration: EmailInboundData,
|
|
|
+ outboundConfiguration: EmailOutboundData,
|
|
|
+ ) => {
|
|
|
+ const validateConfigurationRoundtripMutation = new MutationHandler(
|
|
|
+ useChannelEmailValidateConfigurationRoundtripMutation(),
|
|
|
+ )
|
|
|
+ const addEmailChannelMutation = new MutationHandler(
|
|
|
+ useChannelEmailAddMutation(),
|
|
|
+ )
|
|
|
+
|
|
|
+ // Transform port field to real number for usage in the mutation.
|
|
|
+ inboundConfiguration.port = Number(inboundConfiguration.port)
|
|
|
+ outboundConfiguration.port = Number(outboundConfiguration.port)
|
|
|
+
|
|
|
+ // Extend inbound configuration with archive information when needed.
|
|
|
+ if (metaInformationInbound.value?.archive) {
|
|
|
+ inboundConfiguration = {
|
|
|
+ ...inboundConfiguration,
|
|
|
+ archive: true,
|
|
|
+ archiveBefore: metaInformationInbound.value.archiveBefore,
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ const roundTripResult = await validateConfigurationRoundtripMutation.send(
|
|
|
+ {
|
|
|
+ inboundConfiguration,
|
|
|
+ outboundConfiguration,
|
|
|
+ emailAddress: account.email,
|
|
|
+ },
|
|
|
+ )
|
|
|
+
|
|
|
+ if (
|
|
|
+ roundTripResult?.channelEmailValidateConfigurationRoundtrip?.success
|
|
|
+ ) {
|
|
|
+ try {
|
|
|
+ const addChannelResult = await addEmailChannelMutation.send({
|
|
|
+ input: {
|
|
|
+ inboundConfiguration,
|
|
|
+ outboundConfiguration,
|
|
|
+ emailAddress: account.email,
|
|
|
+ emailRealname: account.realname,
|
|
|
+ },
|
|
|
+ })
|
|
|
+
|
|
|
+ if (addChannelResult?.channelEmailAdd?.channel) {
|
|
|
+ onSuccessCallback()
|
|
|
+ }
|
|
|
+ } catch (errors) {
|
|
|
+ emailChannelForms.emailAccount.setErrors(errors as MutationSendError)
|
|
|
+ setActiveStep('account')
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } catch (errors) {
|
|
|
+ if (
|
|
|
+ errors instanceof UserError &&
|
|
|
+ Object.keys(errors.getFieldErrorList()).length > 0
|
|
|
+ ) {
|
|
|
+ if (
|
|
|
+ Object.keys(errors.getFieldErrorList()).some((key) =>
|
|
|
+ key.startsWith('outbound'),
|
|
|
+ )
|
|
|
+ ) {
|
|
|
+ setActiveStep('outbound')
|
|
|
+ emailChannelForms.emailOutbound.setErrors(errors as MutationSendError)
|
|
|
+ } else {
|
|
|
+ setActiveStep('inbound')
|
|
|
+ emailChannelForms.emailInbound.setErrors(errors as MutationSendError)
|
|
|
+ }
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ emailChannelForms.emailAccount.setErrors(
|
|
|
+ new UserError([
|
|
|
+ {
|
|
|
+ message: i18n.t(
|
|
|
+ 'Email sending and receiving could not be verified. Please check your settings.',
|
|
|
+ ),
|
|
|
+ },
|
|
|
+ ]),
|
|
|
+ )
|
|
|
+ setActiveStep('account')
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ const guessEmailAccount = (data: FormSubmitData<EmailAccountData>) => {
|
|
|
+ loading.value = true
|
|
|
+
|
|
|
+ const guessConfigurationMutation = new MutationHandler(
|
|
|
+ useChannelEmailGuessConfigurationMutation(),
|
|
|
+ )
|
|
|
+
|
|
|
+ return guessConfigurationMutation
|
|
|
+ .send({
|
|
|
+ emailAddress: data.email,
|
|
|
+ password: data.password,
|
|
|
+ })
|
|
|
+ .then(async (result) => {
|
|
|
+ if (
|
|
|
+ result?.channelEmailGuessConfiguration?.result.inboundConfiguration &&
|
|
|
+ result?.channelEmailGuessConfiguration?.result.outboundConfiguration
|
|
|
+ ) {
|
|
|
+ const inboundConfiguration = result.channelEmailGuessConfiguration
|
|
|
+ .result.inboundConfiguration as SetOptional<
|
|
|
+ SetNonNullable<Required<ChannelEmailInboundConfiguration>>,
|
|
|
+ '__typename'
|
|
|
+ >
|
|
|
+ delete inboundConfiguration.__typename
|
|
|
+
|
|
|
+ const outboundConfiguration = result.channelEmailGuessConfiguration
|
|
|
+ .result.outboundConfiguration as SetOptional<
|
|
|
+ SetNonNullable<Required<ChannelEmailOutboundConfiguration>>,
|
|
|
+ '__typename'
|
|
|
+ >
|
|
|
+ delete outboundConfiguration.__typename
|
|
|
+
|
|
|
+ emailChannelForms.emailInbound.updateFieldValues(inboundConfiguration)
|
|
|
+ emailChannelForms.emailOutbound.updateFieldValues(
|
|
|
+ outboundConfiguration,
|
|
|
+ )
|
|
|
+
|
|
|
+ const mailboxStats =
|
|
|
+ result?.channelEmailGuessConfiguration?.result.mailboxStats
|
|
|
+
|
|
|
+ if (
|
|
|
+ mailboxStats?.contentMessages &&
|
|
|
+ mailboxStats?.contentMessages > 0
|
|
|
+ ) {
|
|
|
+ updateMetaInformationInbound(mailboxStats, 'roundtrip')
|
|
|
+ setActiveStep('inbound-messages')
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ await validateConfigurationRoundtripAndChannelAdd(
|
|
|
+ data,
|
|
|
+ inboundConfiguration,
|
|
|
+ outboundConfiguration,
|
|
|
+ )
|
|
|
+ } else {
|
|
|
+ emailChannelForms.emailInbound.updateFieldValues({
|
|
|
+ user: data.email,
|
|
|
+ password: data.password,
|
|
|
+ })
|
|
|
+ emailChannelForms.emailOutbound.updateFieldValues({
|
|
|
+ user: data.email,
|
|
|
+ password: data.password,
|
|
|
+ })
|
|
|
+
|
|
|
+ emailChannelForms.emailInbound.setErrors(
|
|
|
+ new UserError([
|
|
|
+ {
|
|
|
+ message: i18n.t(
|
|
|
+ 'The server settings could not be automatically detected. Please configure them manually.',
|
|
|
+ ),
|
|
|
+ },
|
|
|
+ ]),
|
|
|
+ )
|
|
|
+
|
|
|
+ setActiveStep('inbound')
|
|
|
+ }
|
|
|
+ })
|
|
|
+ .finally(() => {
|
|
|
+ loading.value = false
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+ const validateEmailInbound = (data: FormSubmitData<EmailInboundData>) => {
|
|
|
+ loading.value = true
|
|
|
+
|
|
|
+ const validationConfigurationInbound = new MutationHandler(
|
|
|
+ useChannelEmailValidateConfigurationInboundMutation(),
|
|
|
+ )
|
|
|
+
|
|
|
+ return validationConfigurationInbound
|
|
|
+ .send({
|
|
|
+ inboundConfiguration: {
|
|
|
+ ...data,
|
|
|
+ port: Number(data.port),
|
|
|
+ },
|
|
|
+ })
|
|
|
+ .then((result) => {
|
|
|
+ if (result?.channelEmailValidateConfigurationInbound?.success) {
|
|
|
+ emailChannelForms.emailOutbound.updateFieldValues({
|
|
|
+ host: data.host,
|
|
|
+ user: data.user,
|
|
|
+ password: data.password,
|
|
|
+ })
|
|
|
+
|
|
|
+ const mailboxStats =
|
|
|
+ result?.channelEmailValidateConfigurationInbound?.mailboxStats
|
|
|
+
|
|
|
+ if (
|
|
|
+ mailboxStats?.contentMessages &&
|
|
|
+ mailboxStats?.contentMessages > 0 &&
|
|
|
+ !data.keepOnServer
|
|
|
+ ) {
|
|
|
+ updateMetaInformationInbound(mailboxStats, 'outbound')
|
|
|
+ setActiveStep('inbound-messages')
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ setActiveStep('outbound')
|
|
|
+ }
|
|
|
+ })
|
|
|
+ .finally(() => {
|
|
|
+ loading.value = false
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+ const importEmailInboundMessages = async (
|
|
|
+ data: FormSubmitData<EmailInboundMessagesData>,
|
|
|
+ ) => {
|
|
|
+ if (metaInformationInbound.value && data.archive) {
|
|
|
+ metaInformationInbound.value.archive = true
|
|
|
+ metaInformationInbound.value.archiveBefore = new Date().toISOString()
|
|
|
+ }
|
|
|
+
|
|
|
+ if (metaInformationInbound.value?.nextAction === 'outbound') {
|
|
|
+ setActiveStep('outbound')
|
|
|
+ }
|
|
|
+
|
|
|
+ if (metaInformationInbound.value?.nextAction === 'roundtrip') {
|
|
|
+ loading.value = true
|
|
|
+
|
|
|
+ await validateConfigurationRoundtripAndChannelAdd(
|
|
|
+ emailChannelForms.emailAccount.values.value,
|
|
|
+ emailChannelForms.emailInbound.values.value,
|
|
|
+ emailChannelForms.emailOutbound.values.value,
|
|
|
+ )
|
|
|
+
|
|
|
+ loading.value = false
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ const validateEmailOutbound = (data: FormSubmitData<EmailOutboundData>) => {
|
|
|
+ loading.value = true
|
|
|
+
|
|
|
+ const validationConfigurationOutbound = new MutationHandler(
|
|
|
+ useChannelEmailValidateConfigurationOutboundMutation(),
|
|
|
+ )
|
|
|
+
|
|
|
+ return validationConfigurationOutbound
|
|
|
+ .send({
|
|
|
+ outboundConfiguration: {
|
|
|
+ ...data,
|
|
|
+ port: Number(data.port),
|
|
|
+ },
|
|
|
+ emailAddress: emailChannelForms.emailAccount.values.value
|
|
|
+ ?.email as string,
|
|
|
+ })
|
|
|
+ .then(async (result) => {
|
|
|
+ if (result?.channelEmailValidateConfigurationOutbound?.success) {
|
|
|
+ await validateConfigurationRoundtripAndChannelAdd(
|
|
|
+ emailChannelForms.emailAccount.values.value,
|
|
|
+ emailChannelForms.emailInbound.values.value,
|
|
|
+ emailChannelForms.emailOutbound.values.value,
|
|
|
+ )
|
|
|
+ }
|
|
|
+ })
|
|
|
+ .finally(() => {
|
|
|
+ loading.value = false
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+ return {
|
|
|
+ debouncedLoading,
|
|
|
+ stepTitle,
|
|
|
+ activeStep,
|
|
|
+ activeForm,
|
|
|
+ guessEmailAccount,
|
|
|
+ validateEmailInbound,
|
|
|
+ importEmailInboundMessages,
|
|
|
+ validateEmailOutbound,
|
|
|
+ }
|
|
|
+}
|