feat: enhance email configuration and validation features
- Implemented mutual exclusivity for SSL and TLS options in the email configuration. - Added comprehensive validation for required fields, port range, email format, and password requirements. - Updated the backend to support reversible encryption for SMTP passwords, ensuring secure handling of sensitive data. - Introduced loading states and improved user feedback in the email configuration UI for better user experience.
This commit is contained in:
@@ -39,12 +39,56 @@
|
||||
}
|
||||
});
|
||||
|
||||
// Tornar SSL e TLS mutuamente exclusivos
|
||||
$effect(() => {
|
||||
if (usarSSL && usarTLS) {
|
||||
// Se ambos estão marcados, priorizar TLS por padrão
|
||||
usarSSL = false;
|
||||
}
|
||||
});
|
||||
|
||||
function toggleSSL() {
|
||||
usarSSL = !usarSSL;
|
||||
if (usarSSL) {
|
||||
usarTLS = false;
|
||||
}
|
||||
}
|
||||
|
||||
function toggleTLS() {
|
||||
usarTLS = !usarTLS;
|
||||
if (usarTLS) {
|
||||
usarSSL = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function salvarConfiguracao() {
|
||||
if (!servidor || !porta || !usuario || !senha || !emailRemetente) {
|
||||
// Validação de campos obrigatórios
|
||||
if (!servidor?.trim() || !porta || !usuario?.trim() || !emailRemetente?.trim() || !nomeRemetente?.trim()) {
|
||||
mostrarMensagem("error", "Preencha todos os campos obrigatórios");
|
||||
return;
|
||||
}
|
||||
|
||||
// Validação de porta (1-65535)
|
||||
const portaNum = Number(porta);
|
||||
if (isNaN(portaNum) || portaNum < 1 || portaNum > 65535) {
|
||||
mostrarMensagem("error", "Porta deve ser um número entre 1 e 65535");
|
||||
return;
|
||||
}
|
||||
|
||||
// Validação de formato de email
|
||||
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||||
if (!emailRegex.test(emailRemetente.trim())) {
|
||||
mostrarMensagem("error", "Email remetente inválido");
|
||||
return;
|
||||
}
|
||||
|
||||
// Validação de senha: obrigatória apenas se não houver configuração existente
|
||||
const temConfigExistente = configAtual?.data?.ativo;
|
||||
if (!temConfigExistente && !senha) {
|
||||
mostrarMensagem("error", "Senha é obrigatória para nova configuração");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!authStore.usuario) {
|
||||
mostrarMensagem("error", "Usuário não autenticado");
|
||||
return;
|
||||
@@ -54,9 +98,9 @@
|
||||
try {
|
||||
const resultado = await client.mutation(api.configuracaoEmail.salvarConfigEmail, {
|
||||
servidor: servidor.trim(),
|
||||
porta: Number(porta),
|
||||
porta: portaNum,
|
||||
usuario: usuario.trim(),
|
||||
senha: senha,
|
||||
senha: senha || "", // Senha vazia será tratada no backend
|
||||
emailRemetente: emailRemetente.trim(),
|
||||
nomeRemetente: nomeRemetente.trim(),
|
||||
usarSSL,
|
||||
@@ -79,16 +123,23 @@
|
||||
}
|
||||
|
||||
async function testarConexao() {
|
||||
if (!servidor || !porta || !usuario || !senha) {
|
||||
if (!servidor?.trim() || !porta || !usuario?.trim() || !senha) {
|
||||
mostrarMensagem("error", "Preencha os dados de conexão antes de testar");
|
||||
return;
|
||||
}
|
||||
|
||||
// Validação de porta
|
||||
const portaNum = Number(porta);
|
||||
if (isNaN(portaNum) || portaNum < 1 || portaNum > 65535) {
|
||||
mostrarMensagem("error", "Porta deve ser um número entre 1 e 65535");
|
||||
return;
|
||||
}
|
||||
|
||||
testando = true;
|
||||
try {
|
||||
const resultado = await client.action(api.configuracaoEmail.testarConexaoSMTP, {
|
||||
const resultado = await client.mutation(api.configuracaoEmail.testarConexaoSMTP, {
|
||||
servidor: servidor.trim(),
|
||||
porta: Number(porta),
|
||||
porta: portaNum,
|
||||
usuario: usuario.trim(),
|
||||
senha: senha,
|
||||
usarSSL,
|
||||
@@ -111,6 +162,9 @@
|
||||
const statusConfig = $derived(
|
||||
configAtual?.data?.ativo ? "Configurado" : "Não configurado"
|
||||
);
|
||||
|
||||
const isLoading = $derived(configAtual === undefined);
|
||||
const hasError = $derived(configAtual === null && !isLoading);
|
||||
</script>
|
||||
|
||||
<div class="container mx-auto px-4 py-6 max-w-4xl">
|
||||
@@ -162,24 +216,35 @@
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<!-- Loading State -->
|
||||
{#if isLoading}
|
||||
<div class="alert alert-info mb-6">
|
||||
<span class="loading loading-spinner loading-sm"></span>
|
||||
<span>Carregando configurações...</span>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<!-- Status -->
|
||||
<div class="alert {configAtual?.data?.ativo ? 'alert-success' : 'alert-warning'} mb-6">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" class="stroke-current shrink-0 w-6 h-6">
|
||||
{#if configAtual?.data?.ativo}
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||
{:else}
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />
|
||||
{/if}
|
||||
</svg>
|
||||
<span>
|
||||
<strong>Status:</strong> {statusConfig}
|
||||
{#if configAtual?.data?.testadoEm}
|
||||
- Última conexão testada em {new Date(configAtual.data.testadoEm).toLocaleString('pt-BR')}
|
||||
{/if}
|
||||
</span>
|
||||
</div>
|
||||
{#if !isLoading}
|
||||
<div class="alert {configAtual?.data?.ativo ? 'alert-success' : 'alert-warning'} mb-6">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" class="stroke-current shrink-0 w-6 h-6">
|
||||
{#if configAtual?.data?.ativo}
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||
{:else}
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />
|
||||
{/if}
|
||||
</svg>
|
||||
<span>
|
||||
<strong>Status:</strong> {statusConfig}
|
||||
{#if configAtual?.data?.testadoEm}
|
||||
- Última conexão testada em {new Date(configAtual.data.testadoEm).toLocaleString('pt-BR')}
|
||||
{/if}
|
||||
</span>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<!-- Formulário -->
|
||||
{#if !isLoading}
|
||||
<div class="card bg-base-100 shadow-xl">
|
||||
<div class="card-body">
|
||||
<h2 class="card-title mb-4">Dados do Servidor SMTP</h2>
|
||||
@@ -294,7 +359,8 @@
|
||||
<label class="label cursor-pointer gap-3">
|
||||
<input
|
||||
type="checkbox"
|
||||
bind:checked={usarSSL}
|
||||
checked={usarSSL}
|
||||
onchange={toggleSSL}
|
||||
class="checkbox checkbox-primary"
|
||||
/>
|
||||
<span class="label-text">Usar SSL (porta 465)</span>
|
||||
@@ -305,7 +371,8 @@
|
||||
<label class="label cursor-pointer gap-3">
|
||||
<input
|
||||
type="checkbox"
|
||||
bind:checked={usarTLS}
|
||||
checked={usarTLS}
|
||||
onchange={toggleTLS}
|
||||
class="checkbox checkbox-primary"
|
||||
/>
|
||||
<span class="label-text">Usar TLS (porta 587)</span>
|
||||
@@ -347,6 +414,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<!-- Exemplos Comuns -->
|
||||
<div class="card bg-base-100 shadow-xl mt-6">
|
||||
|
||||
@@ -325,7 +325,7 @@
|
||||
nome: destinatario.nome,
|
||||
matricula: destinatario.matricula,
|
||||
},
|
||||
enviadoPorId: destinatario._id as any,
|
||||
enviadoPorId: authStore.usuario._id as Id<"usuarios">,
|
||||
agendadaPara: agendadaPara,
|
||||
});
|
||||
}
|
||||
@@ -335,7 +335,7 @@
|
||||
destinatarioId: destinatario._id as any,
|
||||
assunto: "Notificação do Sistema",
|
||||
corpo: mensagemPersonalizada,
|
||||
enviadoPorId: destinatario._id as any,
|
||||
enviadoPorId: authStore.usuario._id as Id<"usuarios">,
|
||||
agendadaPara: agendadaPara,
|
||||
});
|
||||
}
|
||||
@@ -433,7 +433,7 @@
|
||||
nome: destinatario.nome,
|
||||
matricula: destinatario.matricula || "",
|
||||
},
|
||||
enviadoPorId: destinatario._id as any,
|
||||
enviadoPorId: authStore.usuario._id as Id<"usuarios">,
|
||||
agendadaPara: agendadaPara,
|
||||
});
|
||||
sucessosEmail++;
|
||||
@@ -446,7 +446,7 @@
|
||||
destinatarioId: destinatario._id as any,
|
||||
assunto: "Notificação do Sistema",
|
||||
corpo: mensagemPersonalizada,
|
||||
enviadoPorId: destinatario._id as any,
|
||||
enviadoPorId: authStore.usuario._id as Id<"usuarios">,
|
||||
agendadaPara: agendadaPara,
|
||||
});
|
||||
sucessosEmail++;
|
||||
|
||||
Reference in New Issue
Block a user