feat: add user authentication UI to header component
Add login and register dialogs, user dropdown menu with profile link and logout option, and integrate with session management hook. Prevent hydration mismatches by conditionally rendering auth elements after mount. Update package-lock.json with new @esbuild dependencies for build tooling.
This commit is contained in:
1 parent
e641138887
commit
55eebde0ba
55 files changed
+7353
-7
No files matched your search
@@ -0,0 +1,199 @@
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import {
|
||||
checkRateLimit,
|
||||
getClientIP,
|
||||
createRateLimitResponse,
|
||||
} from "@/lib/rate-limiter";
|
||||
import { secureLogger } from "@/lib/secure-logger";
|
||||
import { loginSchema } from "@/lib/validation-schemas";
|
||||
import { comparePassword } from "@/lib/auth/password";
|
||||
import {
|
||||
generateAccessToken,
|
||||
generateRefreshToken,
|
||||
} from "@/lib/auth/jwt";
|
||||
import {
|
||||
getUserByEmail,
|
||||
getUserByUsername,
|
||||
updateLastLogin,
|
||||
} from "@/lib/db/queries/users";
|
||||
import { setSession } from "@/lib/redis/session";
|
||||
import { randomUUID } from "crypto";
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
try {
|
||||
// Rate limiting
|
||||
const ip = getClientIP(request);
|
||||
const rateLimit = checkRateLimit(ip, "/api/auth/login");
|
||||
|
||||
if (!rateLimit.allowed) {
|
||||
return createRateLimitResponse(rateLimit.resetAt);
|
||||
}
|
||||
|
||||
const body = await request.json();
|
||||
|
||||
secureLogger.info("📥 POST /api/auth/login - Recebido", {
|
||||
ip,
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
|
||||
// Validação com Zod
|
||||
const validationResult = loginSchema.safeParse(body);
|
||||
|
||||
if (!validationResult.success) {
|
||||
const errors = validationResult.error.issues.map((err) => ({
|
||||
field: err.path.join("."),
|
||||
message: err.message,
|
||||
}));
|
||||
|
||||
return NextResponse.json(
|
||||
{
|
||||
success: false,
|
||||
error: "Dados de entrada inválidos",
|
||||
details: errors,
|
||||
},
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
|
||||
const { emailOrUsername, password } = validationResult.data;
|
||||
|
||||
// Buscar usuário por email ou username
|
||||
const user =
|
||||
(await getUserByEmail(emailOrUsername)) ||
|
||||
(await getUserByUsername(emailOrUsername));
|
||||
|
||||
if (!user) {
|
||||
secureLogger.warn("⚠️ Tentativa de login com credenciais inválidas", {
|
||||
emailOrUsername: secureLogger.mask({ emailOrUsername }),
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
|
||||
return NextResponse.json(
|
||||
{
|
||||
success: false,
|
||||
error: "Email/username ou senha incorretos",
|
||||
},
|
||||
{ status: 401 }
|
||||
);
|
||||
}
|
||||
|
||||
// Verificar se usuário está ativo
|
||||
if (!user.is_active) {
|
||||
return NextResponse.json(
|
||||
{
|
||||
success: false,
|
||||
error: "Conta desativada. Entre em contato com o suporte.",
|
||||
},
|
||||
{ status: 403 }
|
||||
);
|
||||
}
|
||||
|
||||
// Verificar senha
|
||||
if (!user.password_hash) {
|
||||
secureLogger.error("❌ Usuário sem password_hash", {
|
||||
userId: user.user_id,
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
|
||||
return NextResponse.json(
|
||||
{
|
||||
success: false,
|
||||
error: "Erro interno. Tente novamente mais tarde.",
|
||||
},
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
|
||||
const passwordMatch = await comparePassword(password, user.password_hash);
|
||||
|
||||
if (!passwordMatch) {
|
||||
secureLogger.warn("⚠️ Senha incorreta", {
|
||||
userId: user.user_id,
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
|
||||
return NextResponse.json(
|
||||
{
|
||||
success: false,
|
||||
error: "Email/username ou senha incorretos",
|
||||
},
|
||||
{ status: 401 }
|
||||
);
|
||||
}
|
||||
|
||||
// Atualizar último login
|
||||
await updateLastLogin(user.user_id);
|
||||
|
||||
// Gerar tokens
|
||||
const accessToken = generateAccessToken(user.user_id, user.username);
|
||||
const refreshToken = generateRefreshToken(user.user_id, user.username);
|
||||
|
||||
// Criar sessão no Redis
|
||||
const sessionId = randomUUID();
|
||||
await setSession(sessionId, {
|
||||
userId: user.user_id,
|
||||
username: user.username,
|
||||
email: user.email,
|
||||
accessToken,
|
||||
createdAt: Date.now(),
|
||||
});
|
||||
|
||||
// Criar resposta
|
||||
const response = NextResponse.json(
|
||||
{
|
||||
success: true,
|
||||
user: {
|
||||
userId: user.user_id,
|
||||
username: user.username,
|
||||
email: user.email,
|
||||
firstName: user.first_name,
|
||||
lastName: user.last_name,
|
||||
accountLevel: user.account_level,
|
||||
hasDonated: user.has_donated,
|
||||
},
|
||||
accessToken,
|
||||
},
|
||||
{ status: 200 }
|
||||
);
|
||||
|
||||
// Adicionar refresh token e sessionId como cookies httpOnly
|
||||
response.cookies.set("refreshToken", refreshToken, {
|
||||
httpOnly: true,
|
||||
secure: process.env.NODE_ENV === "production",
|
||||
sameSite: "lax",
|
||||
maxAge: 60 * 60 * 24 * 7, // 7 dias
|
||||
path: "/",
|
||||
});
|
||||
|
||||
response.cookies.set("sessionId", sessionId, {
|
||||
httpOnly: true,
|
||||
secure: process.env.NODE_ENV === "production",
|
||||
sameSite: "lax",
|
||||
maxAge: 60 * 60 * 24 * 7, // 7 dias
|
||||
path: "/",
|
||||
});
|
||||
|
||||
secureLogger.info("✅ Login realizado com sucesso", {
|
||||
userId: user.user_id,
|
||||
username: user.username,
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
|
||||
return response;
|
||||
} catch (error) {
|
||||
secureLogger.error("❌ Erro ao fazer login", {
|
||||
error: error instanceof Error ? error.message : "Erro desconhecido",
|
||||
stack: error instanceof Error ? error.stack : undefined,
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
|
||||
return NextResponse.json(
|
||||
{
|
||||
success: false,
|
||||
error: error instanceof Error ? error.message : "Erro interno",
|
||||
},
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { secureLogger } from "@/lib/secure-logger";
|
||||
import { requireAuth } from "@/lib/auth/middleware";
|
||||
import { getUserById } from "@/lib/db/queries/users";
|
||||
import { getUserCharacters } from "@/lib/db/queries/characters";
|
||||
|
||||
export async function GET(request: NextRequest) {
|
||||
try {
|
||||
// Middleware de autenticação
|
||||
const authResult = await requireAuth(request);
|
||||
|
||||
// Se retornou NextResponse, é um erro de autenticação
|
||||
if (authResult instanceof NextResponse) {
|
||||
return authResult;
|
||||
}
|
||||
|
||||
const auth = authResult;
|
||||
|
||||
// Buscar dados do usuário
|
||||
const user = await getUserById(auth.userId);
|
||||
|
||||
if (!user) {
|
||||
return NextResponse.json(
|
||||
{
|
||||
success: false,
|
||||
error: "Usuário não encontrado",
|
||||
},
|
||||
{ status: 404 }
|
||||
);
|
||||
}
|
||||
|
||||
// Buscar personagens do usuário
|
||||
const characters = await getUserCharacters(auth.userId);
|
||||
|
||||
secureLogger.info("✅ Perfil do usuário recuperado", {
|
||||
userId: auth.userId,
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
|
||||
return NextResponse.json(
|
||||
{
|
||||
success: true,
|
||||
user: {
|
||||
userId: user.user_id,
|
||||
username: user.username,
|
||||
email: user.email,
|
||||
firstName: user.first_name,
|
||||
lastName: user.last_name,
|
||||
phone: user.phone,
|
||||
cpf: user.cpf,
|
||||
accountLevel: user.account_level,
|
||||
hasDonated: user.has_donated,
|
||||
whatsappNotifications: user.whatsapp_notifications,
|
||||
createdAt: user.created_at,
|
||||
updatedAt: user.updated_at,
|
||||
lastLogin: user.last_login,
|
||||
isActive: user.is_active,
|
||||
isVerified: user.is_verified,
|
||||
},
|
||||
characters: characters.map((char) => ({
|
||||
characterId: char.character_id,
|
||||
characterName: char.character_name,
|
||||
serverName: char.server_name,
|
||||
isMain: char.is_main,
|
||||
verified: char.verified,
|
||||
createdAt: char.created_at,
|
||||
})),
|
||||
},
|
||||
{ status: 200 }
|
||||
);
|
||||
} catch (error) {
|
||||
// Se for NextResponse (erro de autenticação), re-lançar
|
||||
if (error instanceof NextResponse) {
|
||||
return error;
|
||||
}
|
||||
|
||||
secureLogger.error("❌ Erro ao buscar perfil do usuário", {
|
||||
error: error instanceof Error ? error.message : "Erro desconhecido",
|
||||
stack: error instanceof Error ? error.stack : undefined,
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
|
||||
return NextResponse.json(
|
||||
{
|
||||
success: false,
|
||||
error: error instanceof Error ? error.message : "Erro interno",
|
||||
},
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import {
|
||||
checkRateLimit,
|
||||
getClientIP,
|
||||
createRateLimitResponse,
|
||||
} from "@/lib/rate-limiter";
|
||||
import { secureLogger } from "@/lib/secure-logger";
|
||||
import { verifyRefreshToken, generateAccessToken } from "@/lib/auth/jwt";
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
try {
|
||||
// Rate limiting
|
||||
const ip = getClientIP(request);
|
||||
const rateLimit = checkRateLimit(ip, "/api/auth/refresh");
|
||||
|
||||
if (!rateLimit.allowed) {
|
||||
return createRateLimitResponse(rateLimit.resetAt);
|
||||
}
|
||||
|
||||
// Extrair refresh token do cookie
|
||||
const refreshToken = request.cookies.get("refreshToken")?.value;
|
||||
|
||||
if (!refreshToken) {
|
||||
return NextResponse.json(
|
||||
{
|
||||
success: false,
|
||||
error: "Refresh token não fornecido",
|
||||
},
|
||||
{ status: 401 }
|
||||
);
|
||||
}
|
||||
|
||||
// Validar refresh token
|
||||
const payload = verifyRefreshToken(refreshToken);
|
||||
|
||||
if (!payload) {
|
||||
// Token inválido - limpar cookie
|
||||
const response = NextResponse.json(
|
||||
{
|
||||
success: false,
|
||||
error: "Refresh token inválido ou expirado",
|
||||
},
|
||||
{ status: 401 }
|
||||
);
|
||||
|
||||
response.cookies.delete("refreshToken");
|
||||
return response;
|
||||
}
|
||||
|
||||
// Gerar novo access token
|
||||
const accessToken = generateAccessToken(payload.userId, payload.username);
|
||||
|
||||
secureLogger.info("✅ Token renovado com sucesso", {
|
||||
userId: payload.userId,
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
|
||||
return NextResponse.json(
|
||||
{
|
||||
success: true,
|
||||
accessToken,
|
||||
},
|
||||
{ status: 200 }
|
||||
);
|
||||
} catch (error) {
|
||||
secureLogger.error("❌ Erro ao renovar token", {
|
||||
error: error instanceof Error ? error.message : "Erro desconhecido",
|
||||
stack: error instanceof Error ? error.stack : undefined,
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
|
||||
return NextResponse.json(
|
||||
{
|
||||
success: false,
|
||||
error: error instanceof Error ? error.message : "Erro interno",
|
||||
},
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,270 @@
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import {
|
||||
checkRateLimit,
|
||||
getClientIP,
|
||||
createRateLimitResponse,
|
||||
} from "@/lib/rate-limiter";
|
||||
import { secureLogger } from "@/lib/secure-logger";
|
||||
import { registerSchema } from "@/lib/validation-schemas";
|
||||
import { hashPassword } from "@/lib/auth/password";
|
||||
import {
|
||||
generateAccessToken,
|
||||
generateRefreshToken,
|
||||
} from "@/lib/auth/jwt";
|
||||
import {
|
||||
getUserByEmail,
|
||||
getUserByUsername,
|
||||
} from "@/lib/db/queries/users";
|
||||
import {
|
||||
isServerActive,
|
||||
incrementUsersForServers,
|
||||
} from "@/lib/db/queries/servers";
|
||||
import { setSession } from "@/lib/redis/session";
|
||||
import { randomUUID } from "crypto";
|
||||
import { getPool } from "@/lib/db";
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
try {
|
||||
// Rate limiting
|
||||
const ip = getClientIP(request);
|
||||
const rateLimit = checkRateLimit(ip, "/api/auth/register");
|
||||
|
||||
if (!rateLimit.allowed) {
|
||||
return createRateLimitResponse(rateLimit.resetAt);
|
||||
}
|
||||
|
||||
const body = await request.json();
|
||||
|
||||
secureLogger.info("📥 POST /api/auth/register - Recebido", {
|
||||
ip,
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
|
||||
// Validação com Zod
|
||||
const validationResult = registerSchema.safeParse(body);
|
||||
|
||||
if (!validationResult.success) {
|
||||
const errors = validationResult.error.issues.map((err) => ({
|
||||
field: err.path.join("."),
|
||||
message: err.message,
|
||||
}));
|
||||
|
||||
secureLogger.error("❌ Validação falhou", {
|
||||
errors: JSON.stringify(errors),
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
|
||||
return NextResponse.json(
|
||||
{
|
||||
success: false,
|
||||
error: "Dados de entrada inválidos",
|
||||
details: errors,
|
||||
},
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
|
||||
const validatedData = validationResult.data;
|
||||
|
||||
// Verificar duplicidade: email
|
||||
const existingEmail = await getUserByEmail(validatedData.email);
|
||||
if (existingEmail) {
|
||||
return NextResponse.json(
|
||||
{
|
||||
success: false,
|
||||
error: "Email já está em uso",
|
||||
},
|
||||
{ status: 409 }
|
||||
);
|
||||
}
|
||||
|
||||
// Verificar duplicidade: username
|
||||
const existingUsername = await getUserByUsername(validatedData.username);
|
||||
if (existingUsername) {
|
||||
return NextResponse.json(
|
||||
{
|
||||
success: false,
|
||||
error: "Username já está em uso",
|
||||
},
|
||||
{ status: 409 }
|
||||
);
|
||||
}
|
||||
|
||||
// Validar servidores: verificar se existem e estão ativos
|
||||
const serverNames = validatedData.servers.map((s) => s.serverName);
|
||||
const uniqueServerNames = [...new Set(serverNames)]; // Remover duplicatas
|
||||
|
||||
for (const serverName of uniqueServerNames) {
|
||||
const isActive = await isServerActive(serverName);
|
||||
if (!isActive) {
|
||||
return NextResponse.json(
|
||||
{
|
||||
success: false,
|
||||
error: `Servidor "${serverName}" não existe ou não está ativo`,
|
||||
},
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Hash da senha
|
||||
const passwordHash = await hashPassword(validatedData.password);
|
||||
|
||||
// Criar usuário e personagens em transação atômica
|
||||
const pool = getPool();
|
||||
const client = await pool.connect();
|
||||
|
||||
let newUser: { userId: number; username: string; email: string };
|
||||
const createdCharacters: Array<{ characterName: string; serverName: string }> = [];
|
||||
|
||||
try {
|
||||
await client.query("BEGIN");
|
||||
|
||||
// Criar usuário
|
||||
const userResult = await client.query<{
|
||||
user_id: number;
|
||||
username: string;
|
||||
email: string;
|
||||
}>(
|
||||
`
|
||||
INSERT INTO users (username, email, password_hash, first_name, last_name, phone, cpf)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7)
|
||||
RETURNING user_id, username, email
|
||||
`,
|
||||
[
|
||||
validatedData.username,
|
||||
validatedData.email,
|
||||
passwordHash,
|
||||
validatedData.firstName,
|
||||
validatedData.lastName,
|
||||
validatedData.phone || null,
|
||||
null, // CPF não é mais obrigatório
|
||||
]
|
||||
);
|
||||
|
||||
newUser = {
|
||||
userId: userResult.rows[0].user_id,
|
||||
username: userResult.rows[0].username,
|
||||
email: userResult.rows[0].email,
|
||||
};
|
||||
|
||||
// Criar personagens (primeiro é main por padrão)
|
||||
for (let i = 0; i < validatedData.servers.length; i++) {
|
||||
const serverData = validatedData.servers[i];
|
||||
const isMain = i === 0 || serverData.isMain === true;
|
||||
|
||||
// Se este personagem será main e não é o primeiro, desmarcar outros
|
||||
if (isMain && i > 0) {
|
||||
await client.query(
|
||||
`
|
||||
UPDATE user_characters
|
||||
SET is_main = FALSE
|
||||
WHERE user_id = $1
|
||||
`,
|
||||
[newUser.userId]
|
||||
);
|
||||
}
|
||||
|
||||
// Inserir personagem
|
||||
const charResult = await client.query<{
|
||||
character_id: number;
|
||||
character_name: string;
|
||||
server_name: string;
|
||||
}>(
|
||||
`
|
||||
INSERT INTO user_characters (user_id, character_name, server_name, is_main)
|
||||
VALUES ($1, $2, $3, $4)
|
||||
RETURNING character_id, character_name, server_name
|
||||
`,
|
||||
[newUser.userId, serverData.characterName, serverData.serverName, isMain]
|
||||
);
|
||||
|
||||
createdCharacters.push({
|
||||
characterName: charResult.rows[0].character_name,
|
||||
serverName: charResult.rows[0].server_name,
|
||||
});
|
||||
}
|
||||
|
||||
// Incrementar total_users dos servidores
|
||||
await incrementUsersForServers(uniqueServerNames);
|
||||
|
||||
await client.query("COMMIT");
|
||||
} catch (error) {
|
||||
await client.query("ROLLBACK");
|
||||
throw error;
|
||||
} finally {
|
||||
client.release();
|
||||
}
|
||||
|
||||
// Gerar tokens
|
||||
const accessToken = generateAccessToken(newUser.userId, newUser.username);
|
||||
const refreshToken = generateRefreshToken(
|
||||
newUser.userId,
|
||||
newUser.username
|
||||
);
|
||||
|
||||
// Criar sessão no Redis
|
||||
const sessionId = randomUUID();
|
||||
await setSession(sessionId, {
|
||||
userId: newUser.userId,
|
||||
username: newUser.username,
|
||||
email: newUser.email,
|
||||
accessToken,
|
||||
createdAt: Date.now(),
|
||||
});
|
||||
|
||||
// Criar resposta
|
||||
const response = NextResponse.json(
|
||||
{
|
||||
success: true,
|
||||
user: {
|
||||
userId: newUser.userId,
|
||||
username: newUser.username,
|
||||
email: newUser.email,
|
||||
},
|
||||
accessToken,
|
||||
},
|
||||
{ status: 201 }
|
||||
);
|
||||
|
||||
// Adicionar refresh token e sessionId como cookies httpOnly
|
||||
response.cookies.set("refreshToken", refreshToken, {
|
||||
httpOnly: true,
|
||||
secure: process.env.NODE_ENV === "production",
|
||||
sameSite: "lax",
|
||||
maxAge: 60 * 60 * 24 * 7, // 7 dias
|
||||
path: "/",
|
||||
});
|
||||
|
||||
response.cookies.set("sessionId", sessionId, {
|
||||
httpOnly: true,
|
||||
secure: process.env.NODE_ENV === "production",
|
||||
sameSite: "lax",
|
||||
maxAge: 60 * 60 * 24 * 7, // 7 dias
|
||||
path: "/",
|
||||
});
|
||||
|
||||
secureLogger.info("✅ Usuário registrado com sucesso", {
|
||||
userId: newUser.userId,
|
||||
username: newUser.username,
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
|
||||
return response;
|
||||
} catch (error) {
|
||||
secureLogger.error("❌ Erro ao registrar usuário", {
|
||||
error: error instanceof Error ? error.message : "Erro desconhecido",
|
||||
stack: error instanceof Error ? error.stack : undefined,
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
|
||||
return NextResponse.json(
|
||||
{
|
||||
success: false,
|
||||
error: error instanceof Error ? error.message : "Erro interno",
|
||||
},
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,136 @@
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { cookies } from "next/headers";
|
||||
import { secureLogger } from "@/lib/secure-logger";
|
||||
import { getSession, refreshSession, deleteSession } from "@/lib/redis/session";
|
||||
import { verifyAccessToken } from "@/lib/auth/jwt";
|
||||
|
||||
/**
|
||||
* GET /api/auth/session
|
||||
* Verifica e retorna dados da sessão atual
|
||||
*/
|
||||
export async function GET(request: NextRequest) {
|
||||
try {
|
||||
// Buscar sessionId do cookie
|
||||
const cookieStore = await cookies();
|
||||
const sessionId = cookieStore.get("sessionId")?.value;
|
||||
|
||||
if (!sessionId) {
|
||||
return NextResponse.json(
|
||||
{
|
||||
success: false,
|
||||
authenticated: false,
|
||||
error: "Nenhuma sessão encontrada",
|
||||
},
|
||||
{ status: 401 }
|
||||
);
|
||||
}
|
||||
|
||||
// Buscar sessão no Redis
|
||||
const session = await getSession(sessionId);
|
||||
|
||||
if (!session) {
|
||||
// Limpar cookie se sessão não existe
|
||||
const response = NextResponse.json(
|
||||
{
|
||||
success: false,
|
||||
authenticated: false,
|
||||
error: "Sessão expirada ou inválida",
|
||||
},
|
||||
{ status: 401 }
|
||||
);
|
||||
|
||||
response.cookies.delete("sessionId");
|
||||
return response;
|
||||
}
|
||||
|
||||
// Verificar se o access token ainda é válido
|
||||
const tokenPayload = verifyAccessToken(session.accessToken);
|
||||
|
||||
if (!tokenPayload) {
|
||||
// Token expirado - remover sessão
|
||||
await deleteSession(sessionId);
|
||||
const response = NextResponse.json(
|
||||
{
|
||||
success: false,
|
||||
authenticated: false,
|
||||
error: "Token expirado",
|
||||
},
|
||||
{ status: 401 }
|
||||
);
|
||||
|
||||
response.cookies.delete("sessionId");
|
||||
return response;
|
||||
}
|
||||
|
||||
// Atualizar TTL da sessão
|
||||
await refreshSession(sessionId);
|
||||
|
||||
return NextResponse.json(
|
||||
{
|
||||
success: true,
|
||||
authenticated: true,
|
||||
user: {
|
||||
userId: session.userId,
|
||||
username: session.username,
|
||||
email: session.email,
|
||||
},
|
||||
},
|
||||
{ status: 200 }
|
||||
);
|
||||
} catch (error) {
|
||||
secureLogger.error("❌ Erro ao verificar sessão", {
|
||||
error: error instanceof Error ? error.message : "Erro desconhecido",
|
||||
stack: error instanceof Error ? error.stack : undefined,
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
|
||||
return NextResponse.json(
|
||||
{
|
||||
success: false,
|
||||
authenticated: false,
|
||||
error: error instanceof Error ? error.message : "Erro interno",
|
||||
},
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* DELETE /api/auth/session
|
||||
* Encerra a sessão atual
|
||||
*/
|
||||
export async function DELETE(request: NextRequest) {
|
||||
try {
|
||||
const cookieStore = await cookies();
|
||||
const sessionId = cookieStore.get("sessionId")?.value;
|
||||
|
||||
if (sessionId) {
|
||||
await deleteSession(sessionId);
|
||||
}
|
||||
|
||||
const response = NextResponse.json(
|
||||
{
|
||||
success: true,
|
||||
message: "Sessão encerrada",
|
||||
},
|
||||
{ status: 200 }
|
||||
);
|
||||
|
||||
response.cookies.delete("sessionId");
|
||||
return response;
|
||||
} catch (error) {
|
||||
secureLogger.error("❌ Erro ao encerrar sessão", {
|
||||
error: error instanceof Error ? error.message : "Erro desconhecido",
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
|
||||
return NextResponse.json(
|
||||
{
|
||||
success: false,
|
||||
error: error instanceof Error ? error.message : "Erro interno",
|
||||
},
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user