Files
sgse-app/packages/backend/convex/http.ts
2025-11-06 11:42:48 -03:00

158 lines
4.6 KiB
TypeScript

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<string, string> = {};
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;