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:
@@ -1,7 +1,8 @@
|
||||
import { v } from "convex/values";
|
||||
import { mutation, query, action } from "./_generated/server";
|
||||
import { hashPassword } from "./auth/utils";
|
||||
import { mutation, query } from "./_generated/server";
|
||||
import { encryptSMTPPassword } from "./auth/utils";
|
||||
import { registrarAtividade } from "./logsAtividades";
|
||||
import { api } from "./_generated/api";
|
||||
|
||||
/**
|
||||
* Obter configuração de email ativa (senha mascarada)
|
||||
@@ -62,8 +63,29 @@ export const salvarConfigEmail = mutation({
|
||||
return { sucesso: false as const, erro: "Email remetente inválido" };
|
||||
}
|
||||
|
||||
// Criptografar senha
|
||||
const senhaHash = await hashPassword(args.senha);
|
||||
// Validar porta
|
||||
if (args.porta < 1 || args.porta > 65535) {
|
||||
return { sucesso: false as const, erro: "Porta deve ser um número entre 1 e 65535" };
|
||||
}
|
||||
|
||||
// Buscar config ativa anterior para manter senha se não fornecida
|
||||
const configAtiva = await ctx.db
|
||||
.query("configuracaoEmail")
|
||||
.withIndex("by_ativo", (q) => q.eq("ativo", true))
|
||||
.first();
|
||||
|
||||
// Determinar senhaHash: usar nova senha se fornecida, senão manter a atual
|
||||
let senhaHash: string;
|
||||
if (args.senha && args.senha.trim().length > 0) {
|
||||
// Nova senha fornecida, criptografar usando criptografia reversível (AES)
|
||||
senhaHash = await encryptSMTPPassword(args.senha);
|
||||
} else if (configAtiva) {
|
||||
// Senha não fornecida, manter a atual (já criptografada)
|
||||
senhaHash = configAtiva.senhaHash;
|
||||
} else {
|
||||
// Sem senha e sem config existente - erro
|
||||
return { sucesso: false as const, erro: "Senha é obrigatória para nova configuração" };
|
||||
}
|
||||
|
||||
// Desativar config anterior
|
||||
const configsAntigas = await ctx.db
|
||||
@@ -105,10 +127,7 @@ export const salvarConfigEmail = mutation({
|
||||
});
|
||||
|
||||
/**
|
||||
* Testar conexão SMTP (action - precisa de Node.js)
|
||||
*
|
||||
* NOTA: Esta action será implementada quando instalarmos nodemailer.
|
||||
* Por enquanto, retorna sucesso simulado para não bloquear o desenvolvimento.
|
||||
* Testar conexão SMTP (mutation que chama action real)
|
||||
*/
|
||||
export const testarConexaoSMTP = mutation({
|
||||
args: {
|
||||
@@ -119,10 +138,65 @@ export const testarConexaoSMTP = mutation({
|
||||
usarSSL: v.boolean(),
|
||||
usarTLS: v.boolean(),
|
||||
},
|
||||
returns: v.union(
|
||||
v.object({ sucesso: v.literal(true) }),
|
||||
v.object({ sucesso: v.literal(false), erro: v.string() })
|
||||
),
|
||||
handler: async (ctx, args) => {
|
||||
// Delegar para a action de Node em arquivo separado
|
||||
// Validações básicas
|
||||
if (!args.servidor || args.servidor.trim().length === 0) {
|
||||
return { sucesso: false as const, erro: "Servidor SMTP não pode estar vazio" };
|
||||
}
|
||||
|
||||
return { sucesso: true };
|
||||
if (!args.porta || args.porta < 1 || args.porta > 65535) {
|
||||
return { sucesso: false as const, erro: "Porta inválida. Deve ser entre 1 e 65535" };
|
||||
}
|
||||
|
||||
if (!args.usuario || args.usuario.trim().length === 0) {
|
||||
return { sucesso: false as const, erro: "Usuário não pode estar vazio" };
|
||||
}
|
||||
|
||||
if (!args.senha || args.senha.trim().length === 0) {
|
||||
return { sucesso: false as const, erro: "Senha não pode estar vazia" };
|
||||
}
|
||||
|
||||
// Validação de SSL/TLS mutuamente exclusivos
|
||||
if (args.usarSSL && args.usarTLS) {
|
||||
return { sucesso: false as const, erro: "SSL e TLS não podem estar habilitados simultaneamente" };
|
||||
}
|
||||
|
||||
// Chamar action de teste real (que usa nodemailer)
|
||||
try {
|
||||
const resultado = await ctx.scheduler.runAfter(0, api.actions.smtp.testarConexao, {
|
||||
servidor: args.servidor,
|
||||
porta: args.porta,
|
||||
usuario: args.usuario,
|
||||
senha: args.senha,
|
||||
usarSSL: args.usarSSL,
|
||||
usarTLS: args.usarTLS,
|
||||
});
|
||||
|
||||
// Se o teste foi bem-sucedido e há uma config ativa, atualizar testadoEm
|
||||
if (resultado.sucesso) {
|
||||
const configAtiva = await ctx.db
|
||||
.query("configuracaoEmail")
|
||||
.withIndex("by_ativo", (q) => q.eq("ativo", true))
|
||||
.first();
|
||||
|
||||
if (configAtiva) {
|
||||
await ctx.db.patch(configAtiva._id, {
|
||||
testadoEm: Date.now(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return resultado;
|
||||
} catch (error: any) {
|
||||
return {
|
||||
sucesso: false as const,
|
||||
erro: error.message || "Erro ao conectar com o servidor SMTP"
|
||||
};
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user