diff --git a/src/env.ts b/src/env.ts index 9f31ea271d456e55de88cfee375f5f1c7c3d4485..4b3b2aa73f5337e7eb2f9563eb4d22259e78d19e 100644 --- a/src/env.ts +++ b/src/env.ts @@ -25,6 +25,7 @@ const EnvSchema = z.object({ AWS_SECRET_ACCESS_KEY: z.string(), AWS_REGION: z.string(), S3_BUCKET: z.string(), + URL: z.string(), }) export type EnvSchema = z.infer<typeof EnvSchema> diff --git a/src/routes/auth.route.ts b/src/routes/auth.route.ts index d53d1579c928ccf96542db1ed5136f4bce095a3e..e8e329fb5817c12e8d76e9755aea59c2335e7a8a 100644 --- a/src/routes/auth.route.ts +++ b/src/routes/auth.route.ts @@ -42,116 +42,156 @@ export const authRouter = new Hono().post( zValidator('json', userSchemas.userInputSchema), async (c) => { try { - const input = c.req.valid('json') - - const userStats = userStatsSchemas.dto.parse(await userStatsService.create()) - input.user_stats_id = userStats.id - const user = userSchemas.userDtoSchema.parse( - await userService.create(input) - ) + const input = await c.req.valid('json'); + console.log("Dados recebidos:", input); - return c.json({ user, userStats }) - } catch (e) { - return c.text('could not create') - } -}) -.post('/recoveryPassword/:id', - async (c) => { - try { - const id: number = +c.req.param('id') + // Criando userStats + console.log("Criando userStats..."); + const userStats = userStatsSchemas.dto.parse(await userStatsService.create()); + console.log("UserStats criado:", userStats); - const user = await userService.findById(id) + input.user_stats_id = userStats.id; - if (!user) { + const user = userSchemas.userDtoSchema.parse(await userService.create(input)); + + await authService.sendConfirmationEmail(user.email, "Confirmação de email", user.name, user.email); + + return c.json({ user, userStats }); + } catch (e) { + console.error("Erro no cadastro:", e); + return c.text('could not create', 500); + } + } +) + .post('/recoveryPassword/:id', + async (c) => { + try { + const id: number = +c.req.param('id') + + const user = await userService.findById(id) + + if (!user) { + return c.json( + createApexError({ + status: 'error', + message: 'Usuário não encontrado', + code: HttpStatus.NOT_FOUND, + path: c.req.routePath, + suggestion: 'Verifique se o e-mail está correto', + }), + HttpStatus.NOT_FOUND + ) + } + + var payload = { + id: id, + email: user.email + }; + + + var secret = user.password + '-' + user.createdAt; + //const resetTicket = await recoveryService.create(user) + + var token = jwt.encode(payload, secret); + + await authService.passwordRecoveryEmail(token, user.email, "Recuperação de senha", user.email) + return c.json({ message: 'E-mail de recuperação enviado com sucesso!' }) + } catch (e) { + console.error(e) return c.json( createApexError({ status: 'error', - message: 'Usuário não encontrado', - code: HttpStatus.NOT_FOUND, + message: 'Erro ao processar solicitação', + code: HttpStatus.INTERNAL_SERVER_ERROR, path: c.req.routePath, - suggestion: 'Verifique se o e-mail está correto', + suggestion: 'Tente novamente mais tarde', }), - HttpStatus.NOT_FOUND + HttpStatus.INTERNAL_SERVER_ERROR ) } + } + ) + .post( + '/reset/password-reset', + zValidator('json', userSchemas.userUpdateSchema), + async (c) => { + try { + const input = await c.req.valid('json'); + const emailArray = c.req.queries('email'); + const token = c.req.queries('token')?.[0] ?? ''; - var payload = { - id: id, - email: user.email - }; + if (!emailArray?.[0] || !token) { + throw new Error('Invalid request: missing email or token'); + } + const email = emailArray[0]; + const user = await userService.findByEmail(email); - var secret = user.password + '-' + user.createdAt; - //const resetTicket = await recoveryService.create(user) + if (!user) { + throw new Error('User not found'); + } - var token = jwt.encode(payload, secret); + const secret = user.password + '-' + String(user.createdAt); - await authService.passwordRecoveryEmail(token, user.email, "Recuperação de senha", user.email) - return c.json({ message: 'E-mail de recuperação enviado com sucesso!' }) - } catch (e) { - console.error(e) - return c.json( - createApexError({ - status: 'error', - message: 'Erro ao processar solicitação', - code: HttpStatus.INTERNAL_SERVER_ERROR, - path: c.req.routePath, - suggestion: 'Tente novamente mais tarde', - }), - HttpStatus.INTERNAL_SERVER_ERROR - ) + // Decodifica o token e verifica a assinatura + let payload; + try { + payload = jwt.decode(token, secret); + } catch (error) { + throw new Error('Invalid or expired token'); + } + + // Atualiza a senha do usuário + user.password = String(input.password); + const updatedUser = await userService.update(user); + + userSchemas.userDtoSchema.parse(updatedUser); + + return c.json({ message: 'Password updated successfully' }); + } catch (e) { + return c.json( + createApexError({ + status: 'error', + message: 'Token expired', + code: HttpStatus.BAD_REQUEST, + path: c.req.routePath, + suggestion: 'Check the input and try again', + }), + HttpStatus.BAD_REQUEST + ); + } } - } -) -.post( - '/reset/password-reset', - zValidator('json', userSchemas.userUpdateSchema), - async (c) => { + ) + .get("/confirm-email", async (c) => { try { - const input = await c.req.valid('json'); - const emailArray = c.req.queries('email'); - const token = c.req.queries('token')?.[0] ?? ''; + const emailArray = c.req.queries("email"); - if (!emailArray?.[0] || !token) { - throw new Error('Invalid request: missing email or token'); + if ( !emailArray?.[0]) { + throw new Error("Invalid request: missing email or token"); } const email = emailArray[0]; const user = await userService.findByEmail(email); if (!user) { - throw new Error('User not found'); - } - - const secret = user.password + '-' + String(user.createdAt); - - // Decodifica o token e verifica a assinatura - let payload; - try { - payload = jwt.decode(token, secret); - } catch (error) { - throw new Error('Invalid or expired token'); + throw new Error("User not found"); } - // Atualiza a senha do usuário - user.password = String(input.password); - const updatedUser = await userService.update(user); - - userSchemas.userDtoSchema.parse(updatedUser); - - return c.json({ message: 'Password updated successfully' }); + await authService.updateConfirmedAt(email); + + return c.redirect("http://mecred.mec.gov.br"); } catch (e) { return c.json( createApexError({ - status: 'error', - message: 'Token expired', + status: "error", + message: "Impossible to verify email", code: HttpStatus.BAD_REQUEST, path: c.req.routePath, - suggestion: 'Check the input and try again', + suggestion: "Check the input and try again", }), HttpStatus.BAD_REQUEST ); } - } -); + }); + diff --git a/src/services/auth.service.ts b/src/services/auth.service.ts index 50aa5f897c8dccc147ce3315ce43c682fd94b19f..527e4361d7162411adba03a43c2291d7f07bb0ce 100644 --- a/src/services/auth.service.ts +++ b/src/services/auth.service.ts @@ -1,7 +1,7 @@ import { Inject, Service } from 'typedi' import { UserService } from './user.service' import { sign } from 'hono/jwt' -import env from '@/env' +import env from '../env.ts'; import { authPayload, verifyPassword, @@ -33,9 +33,100 @@ export class AuthService { return token } - async passwordRecoveryEmail(resetTicket: string, receiver: string, subject: string, userEmail: string) { - const token_link: string = `http://mecreddev:3002/novaSenha?token=${resetTicket}&email=${userEmail}`; - const text = ` + async updateConfirmedAt(userEmail: string) { + const user = await this.userService.findByEmail(userEmail); + + if (!user) { + throw new Error("Usuário não encontrado"); + } + + if (user.confirmed_at !== null) { + throw new Error("Usuário já foi confirmado"); + } + + user.confirmed_at = new Date().toISOString(); + return await this.userService.update(user); + } + + + + async sendConfirmationEmail(receiver: string, subject: string, userName: string, userEmail: string) { + const link: string = `${env.URL}/entrar`; + const text = ` + <head> + <style> + + .container { + display: block; + text-align: left; + background-color: rgb(240, 240, 240); + width: 90%; + margin: 0 auto; + padding: 30px 30px; + border-radius: 30%, + font-family: Arial, Helvetica, sans-serif; + } + + button { + background-color: #00bacc; + color: white; + border: none; + border-radius: 5px; + padding: 10px; + font-size: 16px; + cursor: pointer; + } + + .container_msg { + text-align: left; + padding: 0px 20px 0px 20px; + background-color: white; + } + + button:hover { + background-color: rgb(7, 29, 65, 0.8); + } + span { + color: #e74c3c; + font-weight: bold; + } + + </style> + </head> + + <body> + <div class="container" > + <a href="${process.env["URL"]}"></a> + <h1 style="color:#00bacc; font-size: 30px; font-weight: 800; margin-top: 5px; margin-bottom: 5px">Recuperação de senha</h1> + <br> + <p>Olá, ${userName}!</p> + <p>Boas-vindas à Plataforma MEC RED!</p> + <p>Este e-mail foi enviado para confirmar que você é o proprietário do endereço de e-mail fornecido no cadastro da MECRED. + Essa etapa é importante para garantir a validade e segurança das informações enviadas. + </p> + <p>Para confirmar sua conta clique no botão abaixo</p> + <a href="${link}"><button>Confirme seu email</button></a> + </div> + </body> +` + + try { + const response = await transporter.sendMail({ + from: "Não responda <no-reply@c3sl.ufpr.br>", + to: receiver, + subject: subject, + html: text + }); + console.log("E-mail enviado:", response); + console.log(userEmail) + } catch (error) { + console.error("Erro ao enviar e-mail:", error); + } + } + + async passwordRecoveryEmail(resetTicket: string, receiver: string, subject: string, userEmail: string) { + const token_link: string = `${env.URL}novaSenha?token=${resetTicket}&email=${userEmail}`; + const text = ` <head> <style> @@ -95,19 +186,19 @@ export class AuthService { </body> ` - - try { - const response = await transporter.sendMail({ - from: "Não responda <no-reply@c3sl.ufpr.br>", - to: receiver, - subject: subject, - html: text - }); - console.log("E-mail enviado:", response); - console.log(userEmail) - } catch (error) { - console.error("Erro ao enviar e-mail:", error); - } + + try { + const response = await transporter.sendMail({ + from: "Não responda <no-reply@c3sl.ufpr.br>", + to: receiver, + subject: subject, + html: text + }); + console.log("E-mail enviado:", response); + console.log(userEmail) + } catch (error) { + console.error("Erro ao enviar e-mail:", error); } - + } + }