297 lines
9.2 KiB
Vue
297 lines
9.2 KiB
Vue
<script setup lang="ts">
|
|
import { ref, computed, watch } from 'vue'
|
|
import type { ProviderObject, ServiceObject } from '@MailManager/models'
|
|
|
|
const props = defineProps<{
|
|
provider: ProviderObject
|
|
service: ServiceObject
|
|
onTest?: () => Promise<{ success: boolean; message: string; details?: any }>
|
|
}>()
|
|
|
|
const emit = defineEmits<{
|
|
'update:service': [value: ServiceObject]
|
|
'tested': [success: boolean]
|
|
}>()
|
|
|
|
// Local state
|
|
const localProvider = ref<ProviderObject>(props.provider)
|
|
const localService = ref<ServiceObject>(props.service)
|
|
const testing = ref(false)
|
|
const testResult = ref<any>(null)
|
|
|
|
// Computed
|
|
const testSuccess = computed(() => testResult.value?.success === true)
|
|
const serviceLocation = computed(() => localService.value.location?.toJson() ?? null)
|
|
const serviceIdentity = computed(() => localService.value.identity?.toJson() ?? null)
|
|
|
|
// Helper functions
|
|
function getAuthIcon(type?: string): string {
|
|
switch (type) {
|
|
case 'NA': return 'mdi-lock-open-variant'
|
|
case 'BA': return 'mdi-account-key'
|
|
case 'TA': return 'mdi-key'
|
|
case 'OA': return 'mdi-shield-account'
|
|
case 'CC': return 'mdi-certificate'
|
|
default: return 'mdi-help-circle'
|
|
}
|
|
}
|
|
|
|
function getAuthLabel(type?: string): string {
|
|
switch (type) {
|
|
case 'NA': return 'No Authentication'
|
|
case 'BA': return 'Username & Password'
|
|
case 'TA': return 'API Token'
|
|
case 'OA': return 'OAuth 2.0'
|
|
case 'CC': return 'Client Certificate'
|
|
default: return 'Unknown'
|
|
}
|
|
}
|
|
|
|
function formatCapabilities(capabilities: any): string {
|
|
if (!capabilities || typeof capabilities !== 'object') return 'N/A'
|
|
|
|
const caps = Object.entries(capabilities)
|
|
.filter(([_, value]) => value === true)
|
|
.map(([key]) => key)
|
|
.slice(0, 5)
|
|
|
|
const total = caps.length
|
|
const display = caps.slice(0, 3).join(', ')
|
|
|
|
if (total > 3) {
|
|
return `${display}, +${total - 3} more`
|
|
}
|
|
return display
|
|
}
|
|
|
|
async function handleTest() {
|
|
if (!localService.value.location || !localService.value.identity) {
|
|
testResult.value = {
|
|
success: false,
|
|
message: 'Missing configuration'
|
|
}
|
|
return
|
|
}
|
|
|
|
testing.value = true
|
|
testResult.value = null
|
|
|
|
try {
|
|
if (!props.onTest) {
|
|
throw new Error('No connection test callback provided')
|
|
}
|
|
|
|
const result = await props.onTest()
|
|
testResult.value = result
|
|
emit('tested', result.success)
|
|
} catch (error: any) {
|
|
testResult.value = {
|
|
success: false,
|
|
message: error.message || 'Connection test failed'
|
|
}
|
|
emit('tested', false)
|
|
} finally {
|
|
testing.value = false
|
|
}
|
|
}
|
|
|
|
// Watch for changes and emit
|
|
watch(localService, () => {
|
|
emit('update:service', localService.value)
|
|
}, { deep: true })
|
|
|
|
watch(
|
|
() => props.provider,
|
|
(provider) => {
|
|
localProvider.value = provider
|
|
}
|
|
)
|
|
|
|
watch(
|
|
() => props.service,
|
|
(service) => {
|
|
localService.value = service
|
|
testResult.value = null
|
|
}
|
|
)
|
|
</script>
|
|
|
|
<template>
|
|
<div class="test-and-save-step">
|
|
<h3 class="text-h6 mb-2">Test & Save</h3>
|
|
<p class="text-body-2 text-medium-emphasis mb-6">
|
|
Test your connection and save the account configuration
|
|
</p>
|
|
|
|
<!-- Configuration Summary -->
|
|
<v-card variant="outlined" class="mb-4">
|
|
<v-card-subtitle>Configuration Summary</v-card-subtitle>
|
|
<v-card-text>
|
|
<v-list density="compact" class="bg-transparent">
|
|
<!-- Account Label -->
|
|
<v-list-item>
|
|
<template #prepend>
|
|
<v-icon>mdi-label</v-icon>
|
|
</template>
|
|
<v-list-item-title>Account Name</v-list-item-title>
|
|
<v-list-item-subtitle>{{ localService.label || localService.primaryAddress?.address || 'New Account' }}</v-list-item-subtitle>
|
|
</v-list-item>
|
|
|
|
<!-- Email Address -->
|
|
<v-list-item>
|
|
<template #prepend>
|
|
<v-icon>mdi-email</v-icon>
|
|
</template>
|
|
<v-list-item-title>Email Address</v-list-item-title>
|
|
<v-list-item-subtitle>{{ localService.primaryAddress?.address }}</v-list-item-subtitle>
|
|
</v-list-item>
|
|
|
|
<!-- Provider -->
|
|
<v-list-item>
|
|
<template #prepend>
|
|
<v-icon>mdi-cloud</v-icon>
|
|
</template>
|
|
<v-list-item-title>Provider</v-list-item-title>
|
|
<v-list-item-subtitle>{{ localProvider.label }}</v-list-item-subtitle>
|
|
</v-list-item>
|
|
|
|
<!-- Location Details -->
|
|
<template v-if="serviceLocation">
|
|
<v-divider class="my-2" />
|
|
|
|
<v-list-item v-if="serviceLocation.type === 'URI'">
|
|
<template #prepend>
|
|
<v-icon>mdi-web</v-icon>
|
|
</template>
|
|
<v-list-item-title>Service URL</v-list-item-title>
|
|
<v-list-item-subtitle>
|
|
{{ serviceLocation.scheme }}://{{ serviceLocation.host }}:{{ serviceLocation.port }}{{ serviceLocation.path || '' }}
|
|
</v-list-item-subtitle>
|
|
</v-list-item>
|
|
|
|
<template v-if="serviceLocation.type === 'SOCKET_SOLE'">
|
|
<v-list-item>
|
|
<template #prepend>
|
|
<v-icon>mdi-server</v-icon>
|
|
</template>
|
|
<v-list-item-title>Server</v-list-item-title>
|
|
<v-list-item-subtitle>{{ serviceLocation.host }}:{{ serviceLocation.port }}</v-list-item-subtitle>
|
|
</v-list-item>
|
|
<v-list-item>
|
|
<template #prepend>
|
|
<v-icon>mdi-shield-lock</v-icon>
|
|
</template>
|
|
<v-list-item-title>Security</v-list-item-title>
|
|
<v-list-item-subtitle>{{ serviceLocation.encryption.toUpperCase() }}</v-list-item-subtitle>
|
|
</v-list-item>
|
|
</template>
|
|
|
|
<template v-if="serviceLocation.type === 'SOCKET_SPLIT'">
|
|
<v-list-item>
|
|
<template #prepend>
|
|
<v-icon>mdi-inbox-arrow-down</v-icon>
|
|
</template>
|
|
<v-list-item-title>Incoming Mail</v-list-item-title>
|
|
<v-list-item-subtitle>
|
|
{{ serviceLocation.inboundHost }}:{{ serviceLocation.inboundPort }} ({{ serviceLocation.inboundEncryption.toUpperCase() }})
|
|
</v-list-item-subtitle>
|
|
</v-list-item>
|
|
<v-list-item>
|
|
<template #prepend>
|
|
<v-icon>mdi-send</v-icon>
|
|
</template>
|
|
<v-list-item-title>Outgoing Mail</v-list-item-title>
|
|
<v-list-item-subtitle>
|
|
{{ serviceLocation.outboundHost }}:{{ serviceLocation.outboundPort }} ({{ serviceLocation.outboundEncryption.toUpperCase() }})
|
|
</v-list-item-subtitle>
|
|
</v-list-item>
|
|
</template>
|
|
</template>
|
|
|
|
<!-- Authentication Method -->
|
|
<v-divider class="my-2" />
|
|
<v-list-item>
|
|
<template #prepend>
|
|
<v-icon>{{ getAuthIcon(serviceIdentity?.type) }}</v-icon>
|
|
</template>
|
|
<v-list-item-title>Authentication</v-list-item-title>
|
|
<v-list-item-subtitle>{{ getAuthLabel(serviceIdentity?.type) }}</v-list-item-subtitle>
|
|
</v-list-item>
|
|
</v-list>
|
|
</v-card-text>
|
|
</v-card>
|
|
|
|
<!-- Account Label Input -->
|
|
<v-text-field
|
|
v-model="localService.label"
|
|
label="Account Name"
|
|
variant="outlined"
|
|
hint="A friendly name for this account (e.g., Work Email)"
|
|
persistent-hint
|
|
prepend-inner-icon="mdi-label"
|
|
class="mb-4"
|
|
/>
|
|
|
|
<!-- Enable Account Toggle -->
|
|
<v-switch
|
|
v-model="localService.enabled"
|
|
label="Enable this account"
|
|
color="primary"
|
|
class="mb-4"
|
|
/>
|
|
|
|
<!-- Test Connection -->
|
|
<div class="mb-4">
|
|
<v-btn
|
|
color="primary"
|
|
variant="outlined"
|
|
size="large"
|
|
block
|
|
:loading="testing"
|
|
:disabled="testSuccess"
|
|
prepend-icon="mdi-connection"
|
|
@click="handleTest"
|
|
>
|
|
{{ testSuccess ? 'Connection Tested Successfully' : 'Test Connection' }}
|
|
</v-btn>
|
|
|
|
<!-- Test Result -->
|
|
<v-alert
|
|
v-if="testResult"
|
|
:type="testResult.success ? 'success' : 'error'"
|
|
variant="tonal"
|
|
class="mt-4"
|
|
>
|
|
<div class="d-flex align-center">
|
|
<v-icon
|
|
:icon="testResult.success ? 'mdi-check-circle' : 'mdi-alert-circle'"
|
|
class="mr-2"
|
|
/>
|
|
<div class="flex-grow-1">
|
|
<div class="font-weight-bold">{{ testResult.message }}</div>
|
|
<div v-if="testResult.details?.latency" class="text-caption">
|
|
Response time: {{ testResult.details.latency }}ms
|
|
</div>
|
|
<div v-if="testResult.details?.protocols" class="text-caption">
|
|
Protocols: {{ Object.keys(testResult.details.protocols).join(', ') }}
|
|
</div>
|
|
<div v-if="testResult.details?.capabilities" class="text-caption mt-1">
|
|
Capabilities: {{ formatCapabilities(testResult.details.capabilities) }}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</v-alert>
|
|
</div>
|
|
|
|
<!-- Save Warning -->
|
|
<v-alert
|
|
v-if="!testSuccess"
|
|
type="warning"
|
|
variant="tonal"
|
|
density="compact"
|
|
>
|
|
<v-icon start>mdi-alert</v-icon>
|
|
Please test the connection before saving
|
|
</v-alert>
|
|
</div>
|
|
</template> |