import { v } from "convex/values"; import { mutation, query, action } from "./_generated/server"; import { hashPassword } from "./auth/utils"; import { registrarAtividade } from "./logsAtividades"; /** * Obter configuração de email ativa (senha mascarada) */ export const obterConfigEmail = query({ args: {}, handler: async (ctx) => { const config = await ctx.db .query("configuracaoEmail") .withIndex("by_ativo", (q) => q.eq("ativo", true)) .first(); if (!config) { return null; } // Retornar config com senha mascarada return { _id: config._id, servidor: config.servidor, porta: config.porta, usuario: config.usuario, senhaHash: "********", // Mascarar senha emailRemetente: config.emailRemetente, nomeRemetente: config.nomeRemetente, usarSSL: config.usarSSL, usarTLS: config.usarTLS, ativo: config.ativo, testadoEm: config.testadoEm, atualizadoEm: config.atualizadoEm, }; }, }); /** * Salvar configuração de email (apenas TI_MASTER) */ export const salvarConfigEmail = mutation({ args: { servidor: v.string(), porta: v.number(), usuario: v.string(), senha: v.string(), emailRemetente: v.string(), nomeRemetente: v.string(), usarSSL: v.boolean(), usarTLS: v.boolean(), configuradoPorId: v.id("usuarios"), }, returns: v.union( v.object({ sucesso: v.literal(true), configId: v.id("configuracaoEmail") }), v.object({ sucesso: v.literal(false), erro: v.string() }) ), handler: async (ctx, args) => { // Validar email const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; if (!emailRegex.test(args.emailRemetente)) { return { sucesso: false as const, erro: "Email remetente inválido" }; } // Criptografar senha const senhaHash = await hashPassword(args.senha); // Desativar config anterior const configsAntigas = await ctx.db .query("configuracaoEmail") .withIndex("by_ativo", (q) => q.eq("ativo", true)) .collect(); for (const config of configsAntigas) { await ctx.db.patch(config._id, { ativo: false }); } // Criar nova config const configId = await ctx.db.insert("configuracaoEmail", { servidor: args.servidor, porta: args.porta, usuario: args.usuario, senhaHash, emailRemetente: args.emailRemetente, nomeRemetente: args.nomeRemetente, usarSSL: args.usarSSL, usarTLS: args.usarTLS, ativo: true, configuradoPor: args.configuradoPorId, atualizadoEm: Date.now(), }); // Log de atividade await registrarAtividade( ctx, args.configuradoPorId, "configurar", "email", JSON.stringify({ servidor: args.servidor, porta: args.porta }), configId ); return { sucesso: true as const, configId }; }, }); /** * 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. */ export const testarConexaoSMTP = action({ args: { servidor: v.string(), porta: v.number(), usuario: v.string(), senha: v.string(), 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) => { // TODO: Implementar teste real com nodemailer // Por enquanto, simula sucesso try { // Validações básicas if (!args.servidor || args.servidor.trim() === "") { return { sucesso: false as const, erro: "Servidor SMTP não pode estar vazio", }; } if (args.porta < 1 || args.porta > 65535) { return { sucesso: false as const, erro: "Porta inválida" }; } // Simular delay de teste await new Promise((resolve) => setTimeout(resolve, 1000)); // Retornar sucesso simulado console.log( "⚠️ AVISO: Teste de conexão SMTP simulado (nodemailer não instalado ainda)" ); return { sucesso: true as const }; } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); return { sucesso: false as const, erro: errorMessage || "Erro ao testar conexão", }; } }, }); /** * Marcar que a configuração foi testada com sucesso */ export const marcarConfigTestada = mutation({ args: { configId: v.id("configuracaoEmail"), }, handler: async (ctx, args) => { await ctx.db.patch(args.configId, { testadoEm: Date.now(), }); }, });