import { httpRouter } from "convex/server"; import { httpAction } from "./_generated/server"; import { internal } from "./_generated/api"; import { getClientIP } from "./utils/getClientIP"; import { v } from "convex/values"; import { authComponent, createAuth } from "./auth"; const http = httpRouter(); /** * Endpoint de teste para debug - retorna todos os headers disponíveis * GET /api/debug/headers */ http.route({ path: "/api/debug/headers", method: "GET", handler: httpAction(async (ctx, request) => { const headers: Record = {}; request.headers.forEach((value, key) => { headers[key] = value; }); const ip = getClientIP(request); return new Response( JSON.stringify({ headers, extractedIP: ip, url: request.url, }), { status: 200, headers: { "Content-Type": "application/json" }, } ); }), }); /** * Endpoint HTTP para login que captura automaticamente o IP do cliente * POST /api/login * Body: { matriculaOuEmail: string, senha: string } */ http.route({ path: "/api/login", method: "POST", handler: httpAction(async (ctx, request) => { try { // Debug: Log todos os headers disponíveis console.log("=== DEBUG: Headers HTTP ==="); const headersEntries: string[] = []; request.headers.forEach((value, key) => { headersEntries.push(`${key}: ${value}`); }); console.log("Headers:", headersEntries.join(", ")); console.log("Request URL:", request.url); // Extrair IP do cliente do request let clientIP = getClientIP(request); console.log("IP extraído:", clientIP); // Se não encontrou IP, tentar obter do URL ou usar valor padrão if (!clientIP) { try { const url = new URL(request.url); // Tentar pegar do query param se disponível const ipParam = url.searchParams.get("client_ip"); if (ipParam && /^(\d{1,3}\.){3}\d{1,3}$/.test(ipParam)) { clientIP = ipParam; console.log("IP obtido do query param:", clientIP); } else { // Se ainda não tiver IP, usar um identificador baseado no timestamp // Isso pelo menos diferencia requisições console.warn("IP não encontrado nos headers. Usando fallback."); clientIP = undefined; // Deixar como undefined para registrar como não disponível } } catch { console.warn("Erro ao processar URL para IP"); } } // Extrair User-Agent const userAgent = request.headers.get("user-agent") || undefined; // Ler body da requisição const body = await request.json(); if (!body.matriculaOuEmail || !body.senha) { return new Response( JSON.stringify({ sucesso: false, erro: "Matrícula/Email e senha são obrigatórios", }), { status: 400, headers: { "Content-Type": "application/json" }, } ); } // Chamar a mutation de login interna com IP e userAgent const resultado = await ctx.runMutation( internal.autenticacao.loginComIP, { matriculaOuEmail: body.matriculaOuEmail, senha: body.senha, ipAddress: clientIP, userAgent: userAgent, } ); return new Response(JSON.stringify(resultado), { status: 200, headers: { "Content-Type": "application/json", "Access-Control-Allow-Origin": "*", "Access-Control-Allow-Methods": "POST, OPTIONS", "Access-Control-Allow-Headers": "Content-Type", }, }); } catch (error) { return new Response( JSON.stringify({ sucesso: false, erro: error instanceof Error ? error.message : "Erro ao processar login", }), { status: 500, headers: { "Content-Type": "application/json" }, } ); } }), }); /** * Endpoint OPTIONS para CORS preflight */ http.route({ path: "/api/login", method: "OPTIONS", handler: httpAction(async () => { return new Response(null, { status: 200, headers: { "Access-Control-Allow-Origin": "*", "Access-Control-Allow-Methods": "POST, OPTIONS", "Access-Control-Allow-Headers": "Content-Type", }, }); }), }); authComponent.registerRoutes(http, createAuth); export default http;