fix: update email configuration handling and improve type safety

- Changed the mutation for testing SMTP connection to use an action for better handling.
- Introduced an internal mutation to update the test timestamp for email configurations.
- Enhanced type safety by specifying document types for user and session queries.
- Improved error handling in the SMTP connection test to provide clearer feedback on failures.
This commit is contained in:
2025-11-04 01:59:08 -03:00
parent 3b89c496c6
commit e6105ae8ea
3 changed files with 46 additions and 26 deletions

View File

@@ -137,7 +137,7 @@
testando = true; testando = true;
try { try {
const resultado = await client.mutation(api.configuracaoEmail.testarConexaoSMTP, { const resultado = await client.action(api.configuracaoEmail.testarConexaoSMTP, {
servidor: servidor.trim(), servidor: servidor.trim(),
porta: portaNum, porta: portaNum,
usuario: usuario.trim(), usuario: usuario.trim(),

View File

@@ -8,7 +8,7 @@ import {
validarSenha, validarSenha,
} from "./auth/utils"; } from "./auth/utils";
import { registrarLogin } from "./logsLogin"; import { registrarLogin } from "./logsLogin";
import { Id } from "./_generated/dataModel"; import { Id, Doc } from "./_generated/dataModel";
import type { QueryCtx } from "./_generated/server"; import type { QueryCtx } from "./_generated/server";
/** /**
@@ -62,6 +62,7 @@ export const login = mutation({
matricula: v.string(), matricula: v.string(),
nome: v.string(), nome: v.string(),
email: v.string(), email: v.string(),
funcionarioId: v.optional(v.id("funcionarios")),
role: v.object({ role: v.object({
_id: v.id("roles"), _id: v.id("roles"),
nome: v.string(), nome: v.string(),
@@ -100,16 +101,19 @@ export const login = mutation({
const isEmail = args.matriculaOuEmail.includes("@"); const isEmail = args.matriculaOuEmail.includes("@");
// Buscar usuário // Buscar usuário
let usuario; let usuario: Doc<"usuarios"> | null = null;
if (isEmail) { if (isEmail) {
usuario = await ctx.db usuario = await ctx.db
.query("usuarios") .query("usuarios")
.withIndex("by_email", (q) => q.eq("email", args.matriculaOuEmail)) .withIndex("by_email", (q) => q.eq("email", args.matriculaOuEmail))
.first(); .first();
} else { } else {
funcionario = await ctx.db.query("funcionarios").withIndex("by_matricula", (q) => q.eq("matricula", args.matriculaOuEmail)).first(); const funcionario: Doc<"funcionarios"> | null = await ctx.db.query("funcionarios").withIndex("by_matricula", (q) => q.eq("matricula", args.matriculaOuEmail)).first();
if (funcionario) { if (funcionario) {
usuario = await ctx.db.get(funcionario.usuarioId); usuario = await ctx.db
.query("usuarios")
.withIndex("by_funcionarioId", (q) => q.eq("funcionarioId", funcionario._id))
.first();
} }
} }
@@ -242,7 +246,7 @@ export const login = mutation({
}); });
// Buscar role do usuário // Buscar role do usuário
const role = await ctx.db.get(usuario.roleId); const role: Doc<"roles"> | null = await ctx.db.get(usuario.roleId);
if (!role) { if (!role) {
return { return {
sucesso: false as const, sucesso: false as const,
@@ -359,6 +363,7 @@ export const verificarSessao = query({
matricula: v.string(), matricula: v.string(),
nome: v.string(), nome: v.string(),
email: v.string(), email: v.string(),
funcionarioId: v.optional(v.id("funcionarios")),
role: v.object({ role: v.object({
_id: v.id("roles"), _id: v.id("roles"),
nome: v.string(), nome: v.string(),
@@ -375,7 +380,7 @@ export const verificarSessao = query({
), ),
handler: async (ctx, args) => { handler: async (ctx, args) => {
// Buscar sessão // Buscar sessão
const sessao = await ctx.db const sessao: Doc<"sessoes"> | null = await ctx.db
.query("sessoes") .query("sessoes")
.withIndex("by_token", (q) => q.eq("token", args.token)) .withIndex("by_token", (q) => q.eq("token", args.token))
.first(); .first();
@@ -395,7 +400,7 @@ export const verificarSessao = query({
} }
// Buscar usuário // Buscar usuário
const usuario = await ctx.db.get(sessao.usuarioId); const usuario: Doc<"usuarios"> | null = await ctx.db.get(sessao.usuarioId);
if (!usuario || !usuario.ativo) { if (!usuario || !usuario.ativo) {
return { return {
valido: false as const, valido: false as const,
@@ -404,7 +409,7 @@ export const verificarSessao = query({
} }
// Buscar role // Buscar role
const role = await ctx.db.get(usuario.roleId); const role: Doc<"roles"> | null = await ctx.db.get(usuario.roleId);
if (!role) { if (!role) {
return { valido: false as const, motivo: "Role não encontrada" }; return { valido: false as const, motivo: "Role não encontrada" };
} }
@@ -416,6 +421,7 @@ export const verificarSessao = query({
matricula: usuario.matricula, matricula: usuario.matricula,
nome: usuario.nome, nome: usuario.nome,
email: usuario.email, email: usuario.email,
funcionarioId: usuario.funcionarioId,
role: { role: {
_id: role._id, _id: role._id,
nome: role.nome, nome: role.nome,
@@ -478,7 +484,7 @@ export const alterarSenha = mutation({
), ),
handler: async (ctx, args) => { handler: async (ctx, args) => {
// Verificar sessão // Verificar sessão
const sessao = await ctx.db const sessao: Doc<"sessoes"> | null = await ctx.db
.query("sessoes") .query("sessoes")
.withIndex("by_token", (q) => q.eq("token", args.token)) .withIndex("by_token", (q) => q.eq("token", args.token))
.first(); .first();
@@ -487,7 +493,7 @@ export const alterarSenha = mutation({
return { sucesso: false as const, erro: "Sessão inválida" }; return { sucesso: false as const, erro: "Sessão inválida" };
} }
const usuario = await ctx.db.get(sessao.usuarioId); const usuario: Doc<"usuarios"> | null = await ctx.db.get(sessao.usuarioId);
if (!usuario) { if (!usuario) {
return { sucesso: false as const, erro: "Usuário não encontrado" }; return { sucesso: false as const, erro: "Usuário não encontrado" };
} }

View File

@@ -1,8 +1,8 @@
import { v } from "convex/values"; import { v } from "convex/values";
import { mutation, query } from "./_generated/server"; import { mutation, query, action, internalMutation } from "./_generated/server";
import { encryptSMTPPassword } from "./auth/utils"; import { encryptSMTPPassword } from "./auth/utils";
import { registrarAtividade } from "./logsAtividades"; import { registrarAtividade } from "./logsAtividades";
import { api } from "./_generated/api"; import { api, internal } from "./_generated/api";
/** /**
* Obter configuração de email ativa (senha mascarada) * Obter configuração de email ativa (senha mascarada)
@@ -127,9 +127,25 @@ export const salvarConfigEmail = mutation({
}); });
/** /**
* Testar conexão SMTP (mutation que chama action real) * Mutation interna para atualizar testadoEm
*/ */
export const testarConexaoSMTP = mutation({ export const atualizarTestadoEm = internalMutation({
args: {
configId: v.id("configuracaoEmail"),
},
returns: v.null(),
handler: async (ctx, args) => {
await ctx.db.patch(args.configId, {
testadoEm: Date.now(),
});
return null;
},
});
/**
* Testar conexão SMTP (action que chama action real)
*/
export const testarConexaoSMTP = action({
args: { args: {
servidor: v.string(), servidor: v.string(),
porta: v.number(), porta: v.number(),
@@ -142,7 +158,7 @@ export const testarConexaoSMTP = mutation({
v.object({ sucesso: v.literal(true) }), v.object({ sucesso: v.literal(true) }),
v.object({ sucesso: v.literal(false), erro: v.string() }) v.object({ sucesso: v.literal(false), erro: v.string() })
), ),
handler: async (ctx, args) => { handler: async (ctx, args): Promise<{ sucesso: true } | { sucesso: false; erro: string }> => {
// Validações básicas // Validações básicas
if (!args.servidor || args.servidor.trim().length === 0) { if (!args.servidor || args.servidor.trim().length === 0) {
return { sucesso: false as const, erro: "Servidor SMTP não pode estar vazio" }; return { sucesso: false as const, erro: "Servidor SMTP não pode estar vazio" };
@@ -167,7 +183,7 @@ export const testarConexaoSMTP = mutation({
// Chamar action de teste real (que usa nodemailer) // Chamar action de teste real (que usa nodemailer)
try { try {
const resultado = await ctx.scheduler.runAfter(0, api.actions.smtp.testarConexao, { const resultado: { sucesso: true } | { sucesso: false; erro: string } = await ctx.runAction(api.actions.smtp.testarConexao, {
servidor: args.servidor, servidor: args.servidor,
porta: args.porta, porta: args.porta,
usuario: args.usuario, usuario: args.usuario,
@@ -178,23 +194,21 @@ export const testarConexaoSMTP = mutation({
// Se o teste foi bem-sucedido e há uma config ativa, atualizar testadoEm // Se o teste foi bem-sucedido e há uma config ativa, atualizar testadoEm
if (resultado.sucesso) { if (resultado.sucesso) {
const configAtiva = await ctx.db const configAtiva = await ctx.runQuery(api.configuracaoEmail.obterConfigEmail, {});
.query("configuracaoEmail")
.withIndex("by_ativo", (q) => q.eq("ativo", true))
.first();
if (configAtiva) { if (configAtiva) {
await ctx.db.patch(configAtiva._id, { await ctx.runMutation(internal.configuracaoEmail.atualizarTestadoEm, {
testadoEm: Date.now(), configId: configAtiva._id,
}); });
} }
} }
return resultado; return resultado;
} catch (error: any) { } catch (error: unknown) {
const errorMessage = error instanceof Error ? error.message : String(error);
return { return {
sucesso: false as const, sucesso: false as const,
erro: error.message || "Erro ao conectar com o servidor SMTP" erro: errorMessage || "Erro ao conectar com o servidor SMTP"
}; };
} }
}, },