feat: enhance login process with IP capture and improved error handling
- Implemented an internal mutation for login that captures the user's IP address and user agent for better security and tracking. - Enhanced the HTTP login endpoint to extract and log client IP, improving the overall authentication process. - Added validation for IP addresses to ensure only valid formats are recorded, enhancing data integrity. - Updated the login mutation to handle rate limiting and user status checks more effectively, providing clearer feedback on login attempts.
This commit is contained in:
@@ -1,5 +1,150 @@
|
||||
import { httpRouter } from "convex/server";
|
||||
|
||||
const http = httpRouter();
|
||||
|
||||
export default http;
|
||||
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";
|
||||
|
||||
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",
|
||||
},
|
||||
});
|
||||
}),
|
||||
});
|
||||
|
||||
export default http;
|
||||
|
||||
Reference in New Issue
Block a user