feat: adiciona fluxo de redefinição de senha e melhora página de configurações

- Adiciona página /recuperar para solicitar redefinição de senha
- Adiciona página /redefinir-senha para definir nova senha com token
- Cria APIs /api/auth/forgot-password e /api/auth/reset-password
- Adiciona notificação por e-mail quando ticket é criado
- Repagina página de configurações removendo informações técnicas
- Adiciona script de teste para todos os tipos de e-mail
- Corrige acentuações em templates de e-mail

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
rever-tecnologia 2025-12-15 10:42:08 -03:00
parent 300179279a
commit 1bc08d3a5f
10 changed files with 1258 additions and 166 deletions

View file

@ -0,0 +1,97 @@
import { NextResponse } from "next/server"
import { hashPassword } from "better-auth/crypto"
import { prisma } from "@/lib/prisma"
export async function POST(request: Request) {
try {
const body = await request.json()
const { token, password } = body
if (!token || typeof token !== "string") {
return NextResponse.json({ error: "Token inválido" }, { status: 400 })
}
if (!password || typeof password !== "string" || password.length < 6) {
return NextResponse.json({ error: "A senha deve ter pelo menos 6 caracteres" }, { status: 400 })
}
// Busca o token de verificação
const verification = await prisma.authVerification.findFirst({
where: {
value: token,
identifier: { startsWith: "password-reset:" },
expiresAt: { gt: new Date() },
},
})
if (!verification) {
return NextResponse.json({ error: "Token inválido ou expirado" }, { status: 400 })
}
// Extrai o userId do identifier
const userId = verification.identifier.replace("password-reset:", "")
// Busca o usuário
const user = await prisma.authUser.findUnique({
where: { id: userId },
})
if (!user) {
return NextResponse.json({ error: "Usuário não encontrado" }, { status: 400 })
}
// Hash da nova senha
const hashedPassword = await hashPassword(password)
// Atualiza a conta do usuário com a nova senha
await prisma.authAccount.updateMany({
where: {
userId: user.id,
providerId: "credential",
},
data: {
password: hashedPassword,
},
})
// Remove o token usado
await prisma.authVerification.delete({
where: { id: verification.id },
})
return NextResponse.json({ success: true })
} catch (error) {
console.error("[RESET_PASSWORD] Erro:", error)
return NextResponse.json({ error: "Erro ao redefinir senha" }, { status: 500 })
}
}
// GET para validar se o token é válido (usado pela página)
export async function GET(request: Request) {
try {
const { searchParams } = new URL(request.url)
const token = searchParams.get("token")
if (!token) {
return NextResponse.json({ valid: false, error: "Token não fornecido" })
}
const verification = await prisma.authVerification.findFirst({
where: {
value: token,
identifier: { startsWith: "password-reset:" },
expiresAt: { gt: new Date() },
},
})
if (!verification) {
return NextResponse.json({ valid: false, error: "Token inválido ou expirado" })
}
return NextResponse.json({ valid: true })
} catch (error) {
console.error("[RESET_PASSWORD] Erro ao validar token:", error)
return NextResponse.json({ valid: false, error: "Erro ao validar token" })
}
}