generated from Nodarx/template
refactor: use split socket location class
Signed-off-by: Sebastian Krupinski <krupinski01@gmail.com>
This commit is contained in:
@@ -1,26 +1,34 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, ref, watch } from 'vue'
|
||||
import { LocationSocketSole } from '@KTXM/MailManager/models/location'
|
||||
import { LocationSocketSplit } from '@KTXM/MailManager/models/location'
|
||||
import { ServiceObject } from '@KTXM/MailManager/models/service'
|
||||
import type {
|
||||
ServiceLocation,
|
||||
ServiceLocationSocketSole,
|
||||
ServiceLocationSocketSplit,
|
||||
} from '@KTXM/MailManager/types/service'
|
||||
import type {
|
||||
ProviderProtocolPanelProps,
|
||||
ProviderProtocolPanelEmits,
|
||||
} from '@KTXM/MailManager/types/integration'
|
||||
|
||||
type ImapEncryption = 'none' | 'ssl' | 'tls' | 'starttls'
|
||||
type Encryption = 'none' | 'ssl' | 'tls' | 'starttls'
|
||||
|
||||
const props = defineProps<ProviderProtocolPanelProps>()
|
||||
const emit = defineEmits<ProviderProtocolPanelEmits>()
|
||||
|
||||
const host = ref('')
|
||||
const encryption = ref<ImapEncryption>('ssl')
|
||||
const port = ref('993')
|
||||
const verifyPeer = ref(true)
|
||||
const verifyHost = ref(true)
|
||||
// ── Inbound (IMAP) ──────────────────────────────────────────────────────────
|
||||
const inboundHost = ref('')
|
||||
const inboundEncryption = ref<Encryption>('ssl')
|
||||
const inboundPort = ref('993')
|
||||
const inboundVerifyPeer = ref(true)
|
||||
const inboundVerifyHost = ref(true)
|
||||
|
||||
// ── Outbound (SMTP) ─────────────────────────────────────────────────────────
|
||||
const outboundHost = ref('')
|
||||
const outboundEncryption = ref<Encryption>('starttls')
|
||||
const outboundPort = ref('587')
|
||||
const outboundVerifyPeer = ref(true)
|
||||
const outboundVerifyHost = ref(true)
|
||||
|
||||
const encryptionOptions = [
|
||||
{ title: 'Implicit TLS (SSL)', value: 'ssl' },
|
||||
@@ -36,23 +44,30 @@ const rules = {
|
||||
return Number.isInteger(numericValue) && numericValue >= 1 && numericValue <= 65535
|
||||
? true
|
||||
: 'Port must be between 1 and 65535'
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
const isValid = computed(() => !!host.value && rules.port(port.value) === true)
|
||||
const inboundValid = computed(() => !!inboundHost.value && rules.port(inboundPort.value) === true)
|
||||
const outboundValid = computed(() => !!outboundHost.value && rules.port(outboundPort.value) === true)
|
||||
const isValid = computed(() => inboundValid.value && outboundValid.value)
|
||||
|
||||
const currentLocation = computed((): ServiceLocationSocketSole | null => {
|
||||
const currentLocation = computed((): ServiceLocationSocketSplit | null => {
|
||||
if (!isValid.value) {
|
||||
return null
|
||||
}
|
||||
|
||||
return {
|
||||
type: 'SOCKET_SOLE',
|
||||
host: host.value,
|
||||
port: Number(port.value),
|
||||
encryption: encryption.value,
|
||||
verifyPeer: verifyPeer.value,
|
||||
verifyHost: verifyHost.value,
|
||||
type: 'SOCKET_SPLIT',
|
||||
inboundHost: inboundHost.value,
|
||||
inboundPort: Number(inboundPort.value),
|
||||
inboundEncryption: inboundEncryption.value,
|
||||
inboundVerifyPeer: inboundVerifyPeer.value,
|
||||
inboundVerifyHost: inboundVerifyHost.value,
|
||||
outboundHost: outboundHost.value,
|
||||
outboundPort: Number(outboundPort.value),
|
||||
outboundEncryption: outboundEncryption.value,
|
||||
outboundVerifyPeer: outboundVerifyPeer.value,
|
||||
outboundVerifyHost: outboundVerifyHost.value,
|
||||
}
|
||||
})
|
||||
|
||||
@@ -80,14 +95,20 @@ watch(
|
||||
return
|
||||
}
|
||||
|
||||
if (nextService.location instanceof LocationSocketSole) {
|
||||
nextService.location.host = location.host
|
||||
nextService.location.port = location.port
|
||||
nextService.location.encryption = location.encryption
|
||||
nextService.location.verifyPeer = location.verifyPeer ?? true
|
||||
nextService.location.verifyHost = location.verifyHost ?? true
|
||||
if (nextService.location instanceof LocationSocketSplit) {
|
||||
const target = nextService.location
|
||||
target.inboundHost = location.inboundHost
|
||||
target.inboundPort = location.inboundPort
|
||||
target.inboundEncryption = location.inboundEncryption
|
||||
target.inboundVerifyPeer = location.inboundVerifyPeer ?? true
|
||||
target.inboundVerifyHost = location.inboundVerifyHost ?? true
|
||||
target.outboundHost = location.outboundHost
|
||||
target.outboundPort = location.outboundPort
|
||||
target.outboundEncryption = location.outboundEncryption
|
||||
target.outboundVerifyPeer = location.outboundVerifyPeer ?? true
|
||||
target.outboundVerifyHost = location.outboundVerifyHost ?? true
|
||||
} else {
|
||||
nextService.location = LocationSocketSole.fromJson(location)
|
||||
nextService.location = LocationSocketSplit.fromJson(location)
|
||||
}
|
||||
|
||||
emit('update:service', nextService)
|
||||
@@ -95,25 +116,34 @@ watch(
|
||||
{ immediate: true, deep: true }
|
||||
)
|
||||
|
||||
watch(encryption, (next, previous) => {
|
||||
const previousDefault = defaultPortFor(previous)
|
||||
if (!port.value || Number(port.value) === previousDefault) {
|
||||
port.value = String(defaultPortFor(next))
|
||||
watch(inboundEncryption, (next, previous) => {
|
||||
const previousDefault = defaultInboundPort(previous)
|
||||
if (!inboundPort.value || Number(inboundPort.value) === previousDefault) {
|
||||
inboundPort.value = String(defaultInboundPort(next))
|
||||
}
|
||||
})
|
||||
|
||||
watch(outboundEncryption, (next, previous) => {
|
||||
const previousDefault = defaultOutboundPort(previous)
|
||||
if (!outboundPort.value || Number(outboundPort.value) === previousDefault) {
|
||||
outboundPort.value = String(defaultOutboundPort(next))
|
||||
}
|
||||
})
|
||||
|
||||
function syncFromLocation(location: ServiceLocation | null) {
|
||||
const socketLocation = getSocketLocation(location)
|
||||
const split = location?.type === 'SOCKET_SPLIT' ? location : null
|
||||
|
||||
host.value = socketLocation?.host ?? ''
|
||||
encryption.value = socketLocation?.encryption ?? 'ssl'
|
||||
port.value = String(socketLocation?.port ?? defaultPortFor(encryption.value))
|
||||
verifyPeer.value = socketLocation?.verifyPeer ?? true
|
||||
verifyHost.value = socketLocation?.verifyHost ?? true
|
||||
}
|
||||
inboundHost.value = split?.inboundHost ?? ''
|
||||
inboundEncryption.value = split?.inboundEncryption ?? 'ssl'
|
||||
inboundPort.value = String(split?.inboundPort ?? defaultInboundPort(inboundEncryption.value))
|
||||
inboundVerifyPeer.value = split?.inboundVerifyPeer ?? true
|
||||
inboundVerifyHost.value = split?.inboundVerifyHost ?? true
|
||||
|
||||
function getSocketLocation(location?: ServiceLocation | null): ServiceLocationSocketSole | null {
|
||||
return location?.type === 'SOCKET_SOLE' ? location : null
|
||||
outboundHost.value = split?.outboundHost ?? ''
|
||||
outboundEncryption.value = split?.outboundEncryption ?? 'starttls'
|
||||
outboundPort.value = String(split?.outboundPort ?? defaultOutboundPort(outboundEncryption.value))
|
||||
outboundVerifyPeer.value = split?.outboundVerifyPeer ?? true
|
||||
outboundVerifyHost.value = split?.outboundVerifyHost ?? true
|
||||
}
|
||||
|
||||
function sameLocation(a: ServiceLocation | null, b: ServiceLocation | null): boolean {
|
||||
@@ -121,30 +151,51 @@ function sameLocation(a: ServiceLocation | null, b: ServiceLocation | null): boo
|
||||
return a === b
|
||||
}
|
||||
|
||||
if (a.type !== 'SOCKET_SOLE' || b.type !== 'SOCKET_SOLE') {
|
||||
if (a.type !== 'SOCKET_SPLIT' || b.type !== 'SOCKET_SPLIT') {
|
||||
return false
|
||||
}
|
||||
|
||||
return a.host === b.host
|
||||
&& a.port === b.port
|
||||
&& a.encryption === b.encryption
|
||||
&& (a.verifyPeer ?? true) === (b.verifyPeer ?? true)
|
||||
&& (a.verifyHost ?? true) === (b.verifyHost ?? true)
|
||||
return a.inboundHost === b.inboundHost
|
||||
&& a.inboundPort === b.inboundPort
|
||||
&& a.inboundEncryption === b.inboundEncryption
|
||||
&& (a.inboundVerifyPeer ?? true) === (b.inboundVerifyPeer ?? true)
|
||||
&& (a.inboundVerifyHost ?? true) === (b.inboundVerifyHost ?? true)
|
||||
&& a.outboundHost === b.outboundHost
|
||||
&& a.outboundPort === b.outboundPort
|
||||
&& a.outboundEncryption === b.outboundEncryption
|
||||
&& (a.outboundVerifyPeer ?? true) === (b.outboundVerifyPeer ?? true)
|
||||
&& (a.outboundVerifyHost ?? true) === (b.outboundVerifyHost ?? true)
|
||||
}
|
||||
|
||||
function defaultPortFor(nextEncryption: ImapEncryption): number {
|
||||
return nextEncryption === 'ssl' || nextEncryption === 'tls' ? 993 : 143
|
||||
function defaultInboundPort(encryption: Encryption): number {
|
||||
return encryption === 'ssl' || encryption === 'tls' ? 993 : 143
|
||||
}
|
||||
|
||||
function defaultOutboundPort(encryption: Encryption): number {
|
||||
if (encryption === 'ssl') {
|
||||
return 465
|
||||
}
|
||||
if (encryption === 'none') {
|
||||
return 25
|
||||
}
|
||||
return 587
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="imap-protocol-panel">
|
||||
<h3 class="text-h6 mb-4">IMAP Connection Settings</h3>
|
||||
<p class="text-body-2 mb-6">Configure the server address, transport security, and certificate verification for your IMAP mailbox.</p>
|
||||
<h3 class="text-h6 mb-4">Connection Settings</h3>
|
||||
<p class="text-body-2 mb-6">Configure the incoming (IMAP) and outgoing (SMTP) servers, transport security, and certificate verification for this mailbox.</p>
|
||||
|
||||
<!-- ── Incoming (IMAP) ─────────────────────────────────────────────────── -->
|
||||
<div class="d-flex align-center mb-4">
|
||||
<v-icon start color="primary">mdi-email-arrow-left</v-icon>
|
||||
<span class="text-subtitle-1 font-weight-medium">Incoming mail (IMAP)</span>
|
||||
</div>
|
||||
|
||||
<v-text-field
|
||||
v-model="host"
|
||||
label="Server Host"
|
||||
v-model="inboundHost"
|
||||
label="IMAP Server Host"
|
||||
hint="For example: imap.example.com"
|
||||
persistent-hint
|
||||
variant="outlined"
|
||||
@@ -157,7 +208,7 @@ function defaultPortFor(nextEncryption: ImapEncryption): number {
|
||||
/>
|
||||
|
||||
<v-select
|
||||
v-model="encryption"
|
||||
v-model="inboundEncryption"
|
||||
:items="encryptionOptions"
|
||||
label="Security"
|
||||
variant="outlined"
|
||||
@@ -166,7 +217,7 @@ function defaultPortFor(nextEncryption: ImapEncryption): number {
|
||||
/>
|
||||
|
||||
<v-text-field
|
||||
v-model="port"
|
||||
v-model="inboundPort"
|
||||
label="Port"
|
||||
hint="Defaults to 993 for TLS/SSL and 143 for plain or STARTTLS"
|
||||
persistent-hint
|
||||
@@ -179,7 +230,7 @@ function defaultPortFor(nextEncryption: ImapEncryption): number {
|
||||
:rules="[rules.required, rules.port]"
|
||||
/>
|
||||
|
||||
<v-expansion-panels class="mt-4">
|
||||
<v-expansion-panels class="mb-6">
|
||||
<v-expansion-panel>
|
||||
<v-expansion-panel-title>
|
||||
<v-icon start>mdi-cog</v-icon>
|
||||
@@ -187,7 +238,7 @@ function defaultPortFor(nextEncryption: ImapEncryption): number {
|
||||
</v-expansion-panel-title>
|
||||
<v-expansion-panel-text>
|
||||
<v-switch
|
||||
v-model="verifyPeer"
|
||||
v-model="inboundVerifyPeer"
|
||||
label="Verify TLS certificate"
|
||||
color="primary"
|
||||
hint="Disable only for trusted internal or test environments"
|
||||
@@ -196,7 +247,7 @@ function defaultPortFor(nextEncryption: ImapEncryption): number {
|
||||
/>
|
||||
|
||||
<v-switch
|
||||
v-model="verifyHost"
|
||||
v-model="inboundVerifyHost"
|
||||
label="Verify certificate hostname"
|
||||
color="primary"
|
||||
hint="Checks that the certificate matches the IMAP host"
|
||||
@@ -207,9 +258,78 @@ function defaultPortFor(nextEncryption: ImapEncryption): number {
|
||||
</v-expansion-panel>
|
||||
</v-expansion-panels>
|
||||
|
||||
<v-alert type="info" variant="tonal">
|
||||
STARTTLS is accepted for compatibility, but the current IMAP client transport does not perform STARTTLS negotiation. Prefer TLS on port 993 when available.
|
||||
</v-alert>
|
||||
<v-divider class="mb-6" />
|
||||
|
||||
<!-- ── Outgoing (SMTP) ─────────────────────────────────────────────────── -->
|
||||
<div class="d-flex align-center mb-4">
|
||||
<v-icon start color="primary">mdi-email-arrow-right</v-icon>
|
||||
<span class="text-subtitle-1 font-weight-medium">Outgoing mail (SMTP)</span>
|
||||
</div>
|
||||
|
||||
<v-text-field
|
||||
v-model="outboundHost"
|
||||
label="SMTP Server Host"
|
||||
hint="For example: smtp.example.com"
|
||||
persistent-hint
|
||||
variant="outlined"
|
||||
prepend-inner-icon="mdi-server-network"
|
||||
class="mb-4"
|
||||
autocomplete="off"
|
||||
autocorrect="off"
|
||||
autocapitalize="none"
|
||||
:rules="[rules.required]"
|
||||
/>
|
||||
|
||||
<v-select
|
||||
v-model="outboundEncryption"
|
||||
:items="encryptionOptions"
|
||||
label="Security"
|
||||
variant="outlined"
|
||||
prepend-inner-icon="mdi-shield-lock"
|
||||
class="mb-4"
|
||||
/>
|
||||
|
||||
<v-text-field
|
||||
v-model="outboundPort"
|
||||
label="Port"
|
||||
hint="Defaults to 465 for implicit TLS, 587 for STARTTLS, and 25 for plain"
|
||||
persistent-hint
|
||||
variant="outlined"
|
||||
prepend-inner-icon="mdi-numeric"
|
||||
class="mb-4"
|
||||
type="number"
|
||||
min="1"
|
||||
max="65535"
|
||||
:rules="[rules.required, rules.port]"
|
||||
/>
|
||||
|
||||
<v-expansion-panels>
|
||||
<v-expansion-panel>
|
||||
<v-expansion-panel-title>
|
||||
<v-icon start>mdi-cog</v-icon>
|
||||
Security Options
|
||||
</v-expansion-panel-title>
|
||||
<v-expansion-panel-text>
|
||||
<v-switch
|
||||
v-model="outboundVerifyPeer"
|
||||
label="Verify TLS certificate"
|
||||
color="primary"
|
||||
hint="Disable only for trusted internal or test environments"
|
||||
persistent-hint
|
||||
class="mb-4"
|
||||
/>
|
||||
|
||||
<v-switch
|
||||
v-model="outboundVerifyHost"
|
||||
label="Verify certificate hostname"
|
||||
color="primary"
|
||||
hint="Checks that the certificate matches the SMTP host"
|
||||
persistent-hint
|
||||
class="mb-4"
|
||||
/>
|
||||
</v-expansion-panel-text>
|
||||
</v-expansion-panel>
|
||||
</v-expansion-panels>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -232,4 +352,4 @@ function defaultPortFor(nextEncryption: ImapEncryption): number {
|
||||
letter-spacing: 0.0178571429em;
|
||||
color: rgba(var(--v-theme-on-surface), 0.7);
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user