import { Router, Request, Response } from "express"; import multer from "multer"; import { startOfWeek, endOfWeek, parseISO } from 'date-fns'; import { ptBR } from 'date-fns/locale'; import { authMiddleware } from "../../middleware/auth"; import { NextCloudService } from "../../services/nextcloud"; import { prisma } from "../prismaClient"; const router = Router(); const upload = multer({ storage: multer.memoryStorage() }); router.use(authMiddleware); // Middleware para logging com informações mais detalhadas router.use((req, res, next) => { const requestId = Math.random().toString(36).substring(7); console.log(`[${new Date().toISOString()}][${requestId}] Request:`, { method: req.method, path: req.path, originalUrl: req.originalUrl, params: req.params, query: req.query, body: req.body }); // Interceptar resposta para logging const oldJson = res.json; res.json = function(data) { console.log(`[${new Date().toISOString()}][${requestId}] Response:`, { statusCode: res.statusCode, data: data }); return oldJson.apply(res, arguments); }; next(); }); // Função auxiliar para validar se uma string é um UUID válido function isValidUUID(uuid: string, isRoute: boolean = false): boolean { // Lista de rotas que não precisam de validação UUID const routeExceptions = ['agendamentos', 'upload']; // Se for uma rota e estiver na lista de exceções, retorna true if (isRoute && (routeExceptions.includes(uuid) || routeExceptions.some(route => uuid.startsWith(route)))) { return true; } if (!uuid) return false; try { const regexUUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i; return regexUUID.test(uuid); } catch (error) { console.error("Erro ao validar UUID:", error); return false; } } // Middleware para validação de UUID em rotas específicas router.param('id', (req, res, next, id) => { const path = req.path.split('/')[1]; // Pega o primeiro segmento da rota após a / if (!isValidUUID(id, true)) { console.error(`ID inválido: ${id} para rota ${path}`); return res.status(400).json({ error: "ID inválido", details: "O formato do ID fornecido não é válido para esta operação" }); } next(); }); // Rota para listar agendamentos router.get("/agendamentos", async (req: Request, res: Response) => { try { const { inicio, fim } = req.query; console.log("[Agendamentos] Buscando agendamentos:", { inicio, fim }); let whereClause = {}; if (inicio && fim) { whereClause = { data: { gte: new Date(inicio as string), lte: new Date(fim as string) } }; } const agendamentos = await prisma.agendamentoAtendimento.findMany({ where: whereClause, include: { cliente: { select: { id: true, nome: true, }, }, usuario: { select: { id: true, nome: true, }, }, }, orderBy: { data: 'asc', }, }); return res.json({ success: true, data: agendamentos.map(ag => ({ id: ag.id, title: `${ag.cliente.nome} - ${ag.observacoes || 'Sem observações'}`, start: ag.data, end: ag.data, clienteId: ag.clienteId, observacoes: ag.observacoes, status: ag.status, usuarioId: ag.usuarioId, usuario: ag.usuario, cliente: ag.cliente })) }); } catch (error) { console.error("[Agendamentos] Erro:", error); return res.status(500).json({ success: false, error: "Erro ao buscar agendamentos", details: error.message }); } }); // Rota para criar um novo agendamento router.post("/agendamentos", async (req: Request, res: Response) => { try { const { clienteId, data, observacoes } = req.body; // Removido tipo temporariamente console.log("[Agendamentos] Criando agendamento:", { clienteId, data, observacoes }); if (!clienteId || !data) { return res.status(400).json({ error: "Dados inválidos", details: "Cliente e data são obrigatórios" }); } // Verificar se o cliente existe const cliente = await prisma.cliente.findUnique({ where: { id: clienteId } }); if (!cliente) { return res.status(404).json({ error: "Cliente não encontrado" }); } // Criar o agendamento sem o campo tipo const agendamento = await prisma.agendamentoAtendimento.create({ data: { clienteId, data: new Date(data), observacoes, status: "AGENDADO", usuarioId: req.user?.id }, include: { cliente: { select: { id: true, nome: true } } } }); console.log("[Agendamentos] Agendamento criado com sucesso:", { id: agendamento.id, data: agendamento.data, clienteNome: agendamento.cliente.nome }); return res.status(201).json(agendamento); } catch (error) { console.error("[Agendamentos] Erro ao criar agendamento:", error); return res.status(500).json({ error: "Erro ao criar agendamento", details: error.message, stack: process.env.NODE_ENV === 'development' ? error.stack : undefined }); } }); // Rota para atualizar um agendamento router.put("/agendamentos/:id", async (req: Request, res: Response) => { try { const { id } = req.params; const { clienteId, data, observacoes, tipo } = req.body; console.log("Atualizando agendamento:", { id, clienteId, data, observacoes, tipo }); // Verificar se o agendamento existe usando a tabela correta const existingAgendamento = await prisma.agendamentoAtendimento.findUnique({ where: { id } }); if (!existingAgendamento) { return res.status(404).json({ error: "Agendamento não encontrado" }); } // Atualizar o agendamento usando a tabela correta const agendamento = await prisma.agendamentoAtendimento.update({ where: { id }, data: { clienteId, data: data ? new Date(data) : undefined, observacoes, tipo }, include: { cliente: { select: { id: true, nome: true } } } }); console.log("Agendamento atualizado:", { id: agendamento.id, data: agendamento.data, clienteNome: agendamento.cliente.nome }); res.json(agendamento); } catch (error) { console.error("Erro ao atualizar agendamento:", error); res.status(500).json({ error: "Erro ao atualizar agendamento", details: error.message }); } }); // Rota para excluir um agendamento router.delete("/agendamentos/:id", async (req: Request, res: Response) => { try { const { id } = req.params; console.log("Excluindo agendamento:", id); // Verificar se o agendamento existe usando a tabela correta const agendamento = await prisma.agendamentoAtendimento.findUnique({ where: { id }, include: { cliente: true } }); if (!agendamento) { console.error("Agendamento não encontrado:", id); return res.status(404).json({ error: "Agendamento não encontrado" }); } console.log("Agendamento encontrado:", { id: agendamento.id, data: agendamento.data, clienteNome: agendamento.cliente?.nome }); // Excluir o agendamento usando a tabela correta await prisma.agendamentoAtendimento.delete({ where: { id } }); console.log("Agendamento excluído com sucesso"); res.status(200).json({ message: "Agendamento excluído com sucesso" }); } catch (error) { console.error("Erro ao excluir agendamento:", error); res.status(500).json({ error: "Erro ao excluir agendamento", details: error.message }); } }); // Rota para atualizar atendimento com anexos router.patch("/:id", async (req: Request, res: Response) => { try { const { id } = req.params; const { anexos, ...data } = req.body; console.log("Atualizando atendimento:", id, { anexos, data }); // Debug log const atendimento = await prisma.atendimento.update({ where: { id }, data: { ...data, ...(anexos && { anexos: { createMany: { data: anexos.map((anexo: any) => ({ nome: anexo.nome, url: anexo.url, tipo: anexo.tipo, tamanho: anexo.tamanho, })), }, }, }), }, include: { anexos: true, cliente: true, }, }); console.log("Atendimento atualizado:", atendimento); // Debug log res.json(atendimento); } catch (error) { console.error("Erro ao atualizar atendimento:", error); res.status(500).json({ error: "Erro ao atualizar atendimento" }); } }); // Nova rota para upload de arquivo via proxy router.post("/upload", upload.single("file"), async (req: Request, res: Response) => { try { if (!req.file || !req.body.clienteNome) { return res .status(400) .json({ error: "Arquivo e nome do cliente são obrigatórios" }); } // Enviar para o NextCloud usando a estrutura de pastas correta const buffer = req.file.buffer; Object.defineProperty(buffer, "name", { value: req.file.originalname, writable: true, enumerable: true, }); const filePath = await NextCloudService.uploadFile( buffer, req.body.clienteNome, "atendimento", req.body.protocolo, ); res.json({ url: filePath }); } catch (error) { console.error("Erro no upload:", error); res.status(500).json({ error: "Erro no upload do arquivo" }); } }); router.get("/:id", async (req: Request, res: Response) => { try { const { id } = req.params; const atendimento = await prisma.atendimento.findUnique({ where: { id: id, }, include: { cliente: true, processo: true, usuario: { select: { id: true, nome: true, oab: true, oabUF: true, }, }, }, }); if (!atendimento) { return res.status(404).json({ error: "Atendimento não encontrado" }); } res.json(atendimento); } catch (error) { console.error("Erro ao buscar atendimento:", error); res.status(500).json({ error: "Erro ao buscar atendimento" }); } }); export default router;