From ad0d9ff5c6cb4a9dee6821f93beb545de8d41cd3 Mon Sep 17 00:00:00 2001 From: inacio gabriel Date: Wed, 16 Jul 2025 14:52:04 -0300 Subject: [PATCH 1/3] update --- csa/.gitignore | 3 +++ csa/src/pages/redefinir/index.tsx | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/csa/.gitignore b/csa/.gitignore index b27442a..b153560 100644 --- a/csa/.gitignore +++ b/csa/.gitignore @@ -40,3 +40,6 @@ yarn-error.log* # typescript *.tsbuildinfo next-env.d.ts + +#dev.db +prisma/dev.db diff --git a/csa/src/pages/redefinir/index.tsx b/csa/src/pages/redefinir/index.tsx index 9d62e97..7594196 100644 --- a/csa/src/pages/redefinir/index.tsx +++ b/csa/src/pages/redefinir/index.tsx @@ -60,7 +60,7 @@ export default function RedefinirSenha() { borderRadius="md" isLoading={isLoading} loadingText="Enviando..." - bg= "#26a96c" + bg= "sec" color= "white" _hover={{ bg: "#38d39f"}} > @@ -82,7 +82,7 @@ export default function RedefinirSenha() { Date: Sun, 27 Jul 2025 01:37:33 -0300 Subject: [PATCH 2/3] =?UTF-8?q?Implementa=C3=A7=C3=A3o=20completa=20do=20s?= =?UTF-8?q?istema=20de=20envio=20de=20e-mail=20para=20redefini=C3=A7=C3=A3?= =?UTF-8?q?o=20de=20senha=20com=20funcionalidade=20integrada=20e=20testes?= =?UTF-8?q?=20realizados=20com=20sucesso.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- csa/.gitignore | 2 +- csa/package-lock.json | 24 +++- csa/package.json | 1 + csa/prisma/dev.db | Bin 36864 -> 45056 bytes .../migration.sql | 10 ++ csa/prisma/migrations/migration_lock.toml | 2 +- csa/prisma/schema.prisma | 6 + csa/src/pages/api/nova_senha.ts | 35 +++++ csa/src/pages/api/redefinir.ts | 55 ++++++++ csa/src/pages/nova_senha/index.tsx | 127 ++++++++++++++++++ csa/src/pages/redefinir/index.tsx | 22 ++- 11 files changed, 271 insertions(+), 13 deletions(-) create mode 100644 csa/prisma/migrations/20250727001919_add_token_redefinicao/migration.sql create mode 100644 csa/src/pages/api/nova_senha.ts create mode 100644 csa/src/pages/api/redefinir.ts create mode 100644 csa/src/pages/nova_senha/index.tsx diff --git a/csa/.gitignore b/csa/.gitignore index b153560..c9b2216 100644 --- a/csa/.gitignore +++ b/csa/.gitignore @@ -42,4 +42,4 @@ yarn-error.log* next-env.d.ts #dev.db -prisma/dev.db +prisma/dev.db diff --git a/csa/package-lock.json b/csa/package-lock.json index cb2710b..d40abd8 100644 --- a/csa/package-lock.json +++ b/csa/package-lock.json @@ -20,6 +20,7 @@ "jsonwebtoken": "^9.0.2", "next": "^15.3.3", "next-themes": "^0.4.6", + "nodemailer": "^7.0.5", "react": "^19.1.0", "react-dom": "^19.1.0", "react-hook-form": "^7.56.4", @@ -764,13 +765,13 @@ } }, "node_modules/@eslint/plugin-kit": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.2.tgz", - "integrity": "sha512-4SaFZCNfJqvk/kenHpI8xvN42DMaoycy4PzKc5otHxRswww1kAt82OlBuwRVLofCACCTZEcla2Ydxv8scMXaTg==", + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.4.tgz", + "integrity": "sha512-Ul5l+lHEcw3L5+k8POx6r74mxEYKG5kOb6Xpy2gCRW6zweT6TEhAf8vhxGgjhqrd/VO/Dirhsb+1hNpD1ue9hw==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@eslint/core": "^0.15.0", + "@eslint/core": "^0.15.1", "levn": "^0.4.1" }, "engines": { @@ -778,9 +779,9 @@ } }, "node_modules/@eslint/plugin-kit/node_modules/@eslint/core": { - "version": "0.15.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.0.tgz", - "integrity": "sha512-b7ePw78tEWWkpgZCDYkbqDOP8dmM6qe+AOC6iuJqlq1R/0ahMAeH3qynpnqKFGkMltrp44ohV4ubGyvLX28tzw==", + "version": "0.15.1", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.1.tgz", + "integrity": "sha512-bkOp+iumZCCbt1K1CmWf0R9pM5yKpDv+ZXtvSyQpudrI9kuFLp+bM2WOPXImuD/ceQuaa8f5pj93Y7zyECIGNA==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -6412,6 +6413,15 @@ "license": "MIT", "peer": true }, + "node_modules/nodemailer": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-7.0.5.tgz", + "integrity": "sha512-nsrh2lO3j4GkLLXoeEksAMgAOqxOv6QumNRVQTJwKH4nuiww6iC2y7GyANs9kRAxCexg3+lTWM3PZ91iLlVjfg==", + "license": "MIT-0", + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", diff --git a/csa/package.json b/csa/package.json index 556c4f9..bc7e06d 100644 --- a/csa/package.json +++ b/csa/package.json @@ -21,6 +21,7 @@ "jsonwebtoken": "^9.0.2", "next": "^15.3.3", "next-themes": "^0.4.6", + "nodemailer": "^7.0.5", "react": "^19.1.0", "react-dom": "^19.1.0", "react-hook-form": "^7.56.4", diff --git a/csa/prisma/dev.db b/csa/prisma/dev.db index 40e6902bcf26d2e4e1cc5e5309fd0f7f4a8ef2df..a52df71a9fd362e2d36bc30c021f2063f2032c60 100644 GIT binary patch delta 2081 zcmbtUO>7%Q6yA;FwBE#?1Q9i*O=_$nHEQk7&(7@50s51;kTfJwVmAm0!tDH{ZX7$! zkD3G_#|ZV>Ryk1A3kSHNR+SGRxb=VlRj6D5Ar1&}sgMvts>G2=(ki8-Ezs4jX7uLG z``&)vdvEpqrq!RCF7D~*aU7@Be)pc8mkyCPY^Tc^x{%mzTFw_Z-{*cAls+vgTURMy*iZ)^1WGGqB;g?zej z?D$arAvkMqwZ5>HYunSZiMIAlsYxK&OWjNzY;?4Dr)u?{m{+VP<3-1bC!`8Ai6W6TMyRasW1Zyz7THkJ0OC-Y zDC9Em5%&=jm{JijN&<>`gcLOl3dOXbSWy_E2qA_s<3{;9&{PMAV?&kiULH7dX$C_~ zVSs}Sgoq2zR{6h0=)-fMfcyN8*5xUTx_@3 zZ#m!&_!Zo0bR4|_p4%OZ1@5-&$9FcxI^E*Dp7R=aMnHR)=NSiF1yWPHe+?XI>5z<-A!XWF5kXrgel%;|ZgpgnnvmZzM5!^3lMh8#O$|*R4#}}*lGlil& zIW|(OEUhSZe1^S9Wn~!VrCcuL7f&4S3#wD4{(t?T>gXE{vYeohB z_z+yTx{iXqZLx0m9xI*~+wE>RoiP@$tq+BmC7J3u?-X0g>c)lj6%w>q>v~>ZP_8?+cval z5EG#_5mE{j7;dpz&h?#%wKxMCSabJ-J%EJ>)-_)d8AgP#Knd`Y+P52=v8Mi3(oabUsXZ+^jHQBwj&^52uYuW>m5Y3V%Ydp)O^FaVZ`T( z`UX;Hv3*If2m*;2q|{dsNoA4T&f1jwSR@QZ$e?0|u*kN6#zfOdG6;>PG*HHlIph0Q zD(FX2Md5)^6CNnZeF3>a0b55(eClmf%WVf72A`#FgGI1ttL0LvmT#^n-6ySw_-2&< op>*wjshc;#tc&<08pbxFJY1l^=`;CIftva)Gl~Do{)6QI1x58=t^fc4 delta 237 zcmZp8z|^pSX@az%5Ca1PClJE`+e95>Ss@0!ssdjA9}FB^vl)1=@o(nk&mGM5 ziEH*|K>>NL&9k{OSvi>a&M@%D@}1c%sBnO9^BFk{Mh+(a^$h$s_}6b1G+4~PdA)w9 z08r>P1OHe4*P8_c?(=hTFfofVh8Cw5acutT&o3at!99V2e;q#yUncJp-Z-99JZ9Vz zfL2s+Z=R4{!Xm=K-@?HEf&V)HG5&M>E&Px8m-24}T9?DmqQLCRIQe9@%;Y8Y2|#5* d4E!_s5AomPU(cV-{{^Tt2&l}8e{xX&IsiNhLYM#m diff --git a/csa/prisma/migrations/20250727001919_add_token_redefinicao/migration.sql b/csa/prisma/migrations/20250727001919_add_token_redefinicao/migration.sql new file mode 100644 index 0000000..c4ea99a --- /dev/null +++ b/csa/prisma/migrations/20250727001919_add_token_redefinicao/migration.sql @@ -0,0 +1,10 @@ +-- CreateTable +CREATE TABLE "TokenRedefinicaoSenha" ( + "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + "email" TEXT NOT NULL, + "token" TEXT NOT NULL, + "expiresAt" DATETIME NOT NULL +); + +-- CreateIndex +CREATE UNIQUE INDEX "TokenRedefinicaoSenha_token_key" ON "TokenRedefinicaoSenha"("token"); diff --git a/csa/prisma/migrations/migration_lock.toml b/csa/prisma/migrations/migration_lock.toml index e1640d1..2a5a444 100644 --- a/csa/prisma/migrations/migration_lock.toml +++ b/csa/prisma/migrations/migration_lock.toml @@ -1,3 +1,3 @@ # Please do not edit this file manually # It should be added in your version-control system (e.g., Git) -provider = "sqlite" \ No newline at end of file +provider = "sqlite" diff --git a/csa/prisma/schema.prisma b/csa/prisma/schema.prisma index 1fc4a1c..19c47ca 100644 --- a/csa/prisma/schema.prisma +++ b/csa/prisma/schema.prisma @@ -27,3 +27,9 @@ model User { email String @unique senha String } +model TokenRedefinicaoSenha { + id Int @id @default(autoincrement()) + email String + token String @unique + expiresAt DateTime +} diff --git a/csa/src/pages/api/nova_senha.ts b/csa/src/pages/api/nova_senha.ts new file mode 100644 index 0000000..e40a20d --- /dev/null +++ b/csa/src/pages/api/nova_senha.ts @@ -0,0 +1,35 @@ +import { prisma } from '../../lib/prisma'; +import { NextApiRequest, NextApiResponse } from 'next'; +import bcrypt from 'bcryptjs'; + +export default async function handler(req: NextApiRequest, res: NextApiResponse) { + if (req.method !== 'POST') + return res.status(405).json({ message: 'Método não permitido!' }); + + const { token, newPassword } = req.body; + + if (!token || !newPassword) { + return res.status(400).json({ message: 'Token e nova senha são obrigatórios!' }); + } + + const tokenRecord = await prisma.tokenRedefinicaoSenha.findUnique({ + where: { token }, + }); + + if (!tokenRecord || tokenRecord.expiresAt < new Date()) { + return res.status(400).json({ message: 'Token inválido ou expirado!' }); + } + + const hashedPassword = await bcrypt.hash(newPassword, 10); + + await prisma.user.update({ + where: { email: tokenRecord.email }, + data: { senha: hashedPassword }, + }); + + await prisma.tokenRedefinicaoSenha.delete({ + where: { token }, + }); + + return res.status(200).json({ message: 'Senha redefinida com sucesso!' }); +} diff --git a/csa/src/pages/api/redefinir.ts b/csa/src/pages/api/redefinir.ts new file mode 100644 index 0000000..9cc28c9 --- /dev/null +++ b/csa/src/pages/api/redefinir.ts @@ -0,0 +1,55 @@ +import { NextApiRequest, NextApiResponse } from 'next'; +import { prisma } from '../../lib/prisma'; +import nodemailer from 'nodemailer'; +import { randomBytes } from 'crypto'; + +export default async function handler(req: NextApiRequest, res: NextApiResponse) { + if (req.method !== 'POST') return res.status(405).json({ error: 'Metodo nao permitido!' }); + + const { email } = req.body; + + if (!email) return res.status(400).json({ error: 'Email é obrigatorio!' }); + + const user = await prisma.user.findUnique({ + where: { email }, + }); + + if (!user) { + return res.status(404).json({ message: "Email não encontrado" }); + } + + const token = randomBytes(32).toString('hex'); + + const expiresAt = new Date(Date.now() + 3600000); // 1 hora de duraçao ate expirar + + await prisma.tokenRedefinicaoSenha.create({ + data: { + email, + token, + expiresAt, + }, + }); + + const resetLink = `${process.env.NEXT_PUBLIC_URL}/nova_senha?token=${token}`; + + const transporter = nodemailer.createTransport({ + host: process.env.SMTP_HOST, + port: Number(process.env.SMTP_PORT), + secure: process.env.SMTP_SECURE === 'true', + auth: { + user: process.env.SMTP_USER, + pass: process.env.SMTP_PASS, + }, + }); + + await transporter.sendMail({ + from: `"Redefinição de Senha" <${process.env.SMTP_USER}>`, + to: email, + subject: 'Redefinição de Senha', + html: `

Você solicitou a redefinição de senha. Clique no link abaixo para redefinir sua senha:

+ Redefinir Senha +

O link é válido por 1 hora.

`, + }); + + return res.status(200).json({ message: 'Email enviado com sucesso!' }); +} \ No newline at end of file diff --git a/csa/src/pages/nova_senha/index.tsx b/csa/src/pages/nova_senha/index.tsx new file mode 100644 index 0000000..9325775 --- /dev/null +++ b/csa/src/pages/nova_senha/index.tsx @@ -0,0 +1,127 @@ +'use client' + +import { Box,Button, Heading,Input,Text,VStack,} from "@chakra-ui/react"; +import { useState, useEffect } from "react"; +import { useRouter } from "next/router"; + +export default function NovaSenhaPage() { + const router = useRouter(); + const [novaSenha, setNovaSenha] = useState(""); + const [confirmacaoSenha, setConfirmacaoSenha] = useState(""); + const [token, setToken] = useState(""); + const [isLoading, setIsLoading] = useState(false); + + useEffect(() => { + const urlToken = router.query.token; + if (typeof urlToken === "string") { + setToken(urlToken); + } + }, [router.query.token]); + + const handleSubmit = async () => { + if (novaSenha !== confirmacaoSenha) { + alert("As senhas não coincidem."); + return; + } + + setIsLoading(true); + + try { + const response = await fetch("/api/nova_senha", { + method: "POST", + headers: { + "Content-Type": "application/json" + }, + body: JSON.stringify({ token, newPassword: novaSenha }) + }); + + const data = await response.json(); + alert(data.message); + + if (response.ok) { + router.push("/login"); + } + + } catch (error) { + console.error("Erro ao redefinir a senha:", error); + alert("Erro ao redefinir a senha."); + } + + setIsLoading(false); + }; + + return ( + + + + Criar nova senha + + + Insira sua nova senha e confirme abaixo. + + + + setNovaSenha(e.target.value)} + borderColor="green.500" + _focus={{ borderColor: "green.700" }} + textAlign="center" + borderRadius="md" + /> + setConfirmacaoSenha(e.target.value)} + borderColor="green.500" + _focus={{ borderColor: "green.700" }} + textAlign="center" + borderRadius="md" + /> + + + + + + + ); +} diff --git a/csa/src/pages/redefinir/index.tsx b/csa/src/pages/redefinir/index.tsx index 7594196..3e9ddb6 100644 --- a/csa/src/pages/redefinir/index.tsx +++ b/csa/src/pages/redefinir/index.tsx @@ -9,10 +9,24 @@ export default function RedefinirSenha() { const handleSubmit = async () => { setIsLoading(true) - // Simula um delay de envio (pode ser substituído por uma chamada API real) - await new Promise(resolve => setTimeout(resolve, 2000)) - alert(`Um link de redefinição foi enviado para: ${email}`) + + try { + const response = await fetch("/api/redefinir",{ + method: "POST", + headers: { + "Content-Type": "application/json" + }, + body: JSON.stringify({ email }) + }) + + const data = await response.json() + alert(data.message) + }catch (error) { + console.error("Erro ao enviar o email de redefinição:", error) + } + setIsLoading(false) + } return ( @@ -70,7 +84,7 @@ export default function RedefinirSenha() { variant="outline" colorScheme="green" width="100" - onClick={() => window.location.href = "/login"} + onClick={() => window.location.href = "/"} textTransform="uppercase" borderRadius="md" From 02f4ec06f5979ae75a9c727f4dd669768d3ce7ea Mon Sep 17 00:00:00 2001 From: inacio gabriel <163515464+Inaciogabriel0@users.noreply.github.com> Date: Mon, 28 Jul 2025 18:42:51 +0000 Subject: [PATCH 3/3] corrigindo conflito no banco de dados --- csa/prisma/dev.db | Bin 45056 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 csa/prisma/dev.db diff --git a/csa/prisma/dev.db b/csa/prisma/dev.db deleted file mode 100644 index a52df71a9fd362e2d36bc30c021f2063f2032c60..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 45056 zcmeI5OKc;_d4SoXD2berI_r&~Q5LgH$h+7T)=gD)clF~C?`kD#hI&vhQW`B_6ZL9} zBa+QwlcUET46Xy8 ziQ`>(U}UlT*O1ff>U#Y3{naJ(-+%wTzq?uM$ZQa{8?=+P5`U1;w8XpFY$B0(Ref)% z?`4`&KgKV2)K+`u_w#PYcp{s7QpljztZ^Fd%jPkybmH8~i98>9 z_}0o^ez}y-mWzemaz0xquI3+P7e*F++R{RHyZD?!3vVs-J6u@o)zg!6uB&}`+M%p2 zM>aV!Gcy#awEW&?{(0SG-^yfFTBwNyC3Go&FTa;9ZkMvf^5$lCXRoleymyq{$RA~w z%cbo?Q8l@hFP6SEs3aS-R$s`L@()ynVWr`rf$$>_$+K3iEu-bmLUvUlD;2i#Pikit z%Tp6`i?iAXeNB`j*{<|^iNVKv>5-hTmK$J=^AyE_jEj}Y$+@LjtvC6!-L5Loqz!r5 z^wjg4ezxe9)-Mk%EPj}tn4FuP)xO^Ct10e1{!U(1LY%emLd;uTRT_YFz6tvv`nFI-DEoF#lLKJC&&3t;V4HdlMr`UoAMm+|N^5 zjH@p!?u<{&z5TX!)F&K0s;g_5N+ch3Ws}QmyQwSOuN{0Vrt~FMbFsIcoSd7R(>{#) zZT7qC|2KZ6{=z_x->2d_wNCvxniI0~)o6*gjv8%(pS>VcKjaQ0msXqswT_>)s1N>O>lc?KRxLR-2)Qwl$=vayh zRV%~Obh$f00e+Q@6}|c+?!676Nyxs3s+i4|`OK6h>G+t{HdA(T19a*63PEJ)xWDjZ3PDF|YiVeFWu;|Ic)t|ZdMz8Mfh@ZXHafhh7cm~ zZO5Zh352-Bo+o@}`^4oQW`R1!sPB80?cjh3VI$0#g@t9?*fWVOO`8#q`zSE6qkZe= zKS#gr3usx`M3$!njIC=?|H$`yS*ifcD{|Dyke+CTyVKmZ5;0U!VbfB+Bx z0zd!=00AHX1b%M>-b$9WPhwF?#sxo#Wn=8kWcd?W9OC=`n*L7-^??KgfB+Bx0zd!= z00AHX1b_e#00KY&2z*Kjd|8`FlxL0U!1FAVGcPw5?HI9Svp%17112BD6HF{X}+gH>x%UJJ8T z%GxzqZ-w=46rG*j8DtN0k!73jR^#^$WBK=8EkIIQhs!?f3SXjSUf%4iTswozjA)~Ft_c# z|6u3*QQ>}cQY)P~#l3Q8=Qww4U2Nju;8Cvk#i>}9yheH{X}T&(8W)By1v6|Wxxrjh zMO*VNPHi3#i(^?2o7qM;I(hOVD`d7M+p^ZC*#_muRD>62@5bTzLlykJ+h&U5GsF%a zEbpC!8=X^k**bDnP;&eI@KJ7Mr@Z#?!CEnRm?!7O)|sf9C49DiFO0d63;R$KTzaIfK>1|iVX{^pmWSsj?mmX9hoNoGdhm{-H-{RtyZy6Y!^1%$IeAz zjh?unmD|l7BH>2G>Y15aec$&tmI~OZbq>rT-RiW=UNWU*ntNGW>ZJ%4oY=@TnCVM3 zv&_Iy0qQ(M1~wuN$CfF5S+BLE=!$tt>9G*i8&cF>dvqUfZ}U^O%W}ZzHdW=!BB<~!(e zHV@aKY==Ik>MbT&L9&JT+!D{tUcj=;WdJw*c(el#rnYDi&qOEXaN1IBN-+n{8JMFkA5P_p+xUf8>N9r3? zO2<&o0R*lJ6v!QXi7*;6EFH^Yx4~q4yD498M(;MGTr-OA|7Y}iLjR?HuK%}JUe|x1|K;zr)I%j800e*l5C8%|00;m9AOHk_01yBIpK}7YlXtaCTV^JcoYw}1 z!*n*8*M6J%Zt7O@6>VrXoBYG1p~hIB&Uf00e*l5C8%| z00;m9Anb}M-|eR=;s_WvKBIZWu^)^jr-%^Xhu z;`5?h7$*<_0zd!=00AHX1b_e#00KbZvrM3xnn97*+FdxCt%1S3rQ&idw$a<%b^#68u~nrz12 z?@`#jpbd48+s6J=Pk4a?i*ONO$xYRYt2UM|TPVaYT2+Ph|Co+UL_G%y^{9=*)U!&`v?O8N zK`4-fxFYa432a;G3E7Mb=6TZcUBP{uB0s>c6L*yUoB00!_{`4|`jY;)Ge6V2diS$D z|6y!E00;m9AOHk_01yBIKmZ5;0U+=xCXgTRO=~Y|mPid+c|nWAc)yhwwIw9`t-PqA OU~E-!Nx!HS;Qs(muOG|+