diff --git a/backend/config/database/database.js b/backend/config/database/database.js index cdda1df..c2323f8 100644 --- a/backend/config/database/database.js +++ b/backend/config/database/database.js @@ -1,6 +1,7 @@ const {Sequelize} = require('sequelize'); const connection = 'postgresql://test_db_owner:Lqn84hbsNPvG@ep-purple-voice-a5bgc10s.us-east-2.aws.neon.tech/test_db?sslmode=require' +// const connection = process.env.DATABASE_URL const sequelize = new Sequelize(connection, { dialect: 'postgres', diff --git a/backend/controller/StripeController.js b/backend/controller/StripeController.js index a5565d3..7f15d4f 100644 --- a/backend/controller/StripeController.js +++ b/backend/controller/StripeController.js @@ -4,7 +4,8 @@ const router = express.Router() const stripe = require('stripe')('sk_test_51PSHknKnVUk9u0R7NE00UeVyiOzpnfGxGYnLG6ViHxy2eOpDXfYCQTU28Xnfuh9MPvg7qwi5hQp4ArEBjJhjv73z005BOmZJSK') // const domain = "http://localhost:5173" -const domain = "http://86.38.204.61" +// const domain = "http://86.38.204.61" +const domain = process.env.FRONTEND_URL router.post('/create-checkout-session', async (req, res) => { //TODO encriptar y parsear a bse64 datos del retorno de la respuesta diff --git a/backend/controller/UserController.js b/backend/controller/UserController.js index 174adce..e06c0b3 100644 --- a/backend/controller/UserController.js +++ b/backend/controller/UserController.js @@ -54,7 +54,8 @@ router.post('/register', async (req, res) => { }) router.post('/login', async (req, res) => { - const { user, password } = req.body + const user = req.body.user + const password = atob(req.body.password) const errors = {} @@ -78,6 +79,7 @@ router.post('/login', async (req, res) => { return res.status(404).json({ message: 'Usuario o contraseña incorrectos' }) } + userFound.password = undefined res.status(200).json(userFound) }) diff --git a/backend/index.js b/backend/index.js index f505ae6..578168d 100644 --- a/backend/index.js +++ b/backend/index.js @@ -1,14 +1,15 @@ -require('dotenv').config() +require('dotenv').config(); +const express = require('express'); +const app = express(); -const express = require("express") const cors = require('cors') const meetController = require('./controller/MeetController') const stripeController = require('./controller/StripeController') const userController = require('./controller/UserController') const purchaseController = require('./controller/PurchaseController') +const authenticateInterceptorMiddleware = require('./interceptor/AuthenticateInterceptorMiddleware'); -const app = express() const port = process.env.PORT ? process.env.PORT : 5000 const corsOptions = { @@ -19,6 +20,7 @@ const corsOptions = { app.use(cors(corsOptions)) app.use(express.json()) +app.use(authenticateInterceptorMiddleware) app.use('/api/meets', meetController) app.use('/api/users', userController) diff --git a/backend/interceptor/AuthenticateInterceptorMiddleware.js b/backend/interceptor/AuthenticateInterceptorMiddleware.js new file mode 100644 index 0000000..931545c --- /dev/null +++ b/backend/interceptor/AuthenticateInterceptorMiddleware.js @@ -0,0 +1,41 @@ +const userService = require("../service/UserService"); + +async function interceptorMiddleware(req, res, next) { + const path = req.path.toLowerCase(); + + const excludedPaths = [ + '/api/users/login', + '/api/users/register' + ]; + + if (!excludedPaths.includes(path)) { + const authHeader = req.headers.authorization; + if (!authHeader || !authHeader.startsWith('Basic ')) { + return res.status(401).json({ message: 'Unauthorized' }); + } + + try { + const authHeaderReplaced = authHeader.replace('Basic ', ''); + const authDecode = Buffer.from(authHeaderReplaced, 'base64').toString('utf-8'); + const [user, password] = authDecode.split(':'); + + // Validar usuario y contraseña + const userFound = await userService.login(user, atob(password)); + + if (!userFound) { + return res.status(401).json({ message: 'Unauthorized' }); + } + + // Agregar el usuario encontrado al objeto req para su uso posterior si es necesario + req.user = userFound; + + } catch (error) { + console.error('Error en middleware de autenticación:', error); + return res.status(500).json({ message: 'Internal Server Error' }); + } + } + + next(); +} + +module.exports = interceptorMiddleware; diff --git a/backend/package-lock.json b/backend/package-lock.json index 412dc1b..3bcef96 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -11,7 +11,7 @@ "dependencies": { "bcrypt": "^5.1.0", "cors": "^2.8.5", - "dotenv": "^16.0.3", + "dotenv": "^16.4.5", "express": "^4.19.2", "express-validator": "^6.14.2", "jsonwebtoken": "^9.0.0", diff --git a/backend/package.json b/backend/package.json index 5310e44..a30541d 100644 --- a/backend/package.json +++ b/backend/package.json @@ -12,7 +12,7 @@ "dependencies": { "bcrypt": "^5.1.0", "cors": "^2.8.5", - "dotenv": "^16.0.3", + "dotenv": "^16.4.5", "express": "^4.19.2", "express-validator": "^6.14.2", "jsonwebtoken": "^9.0.0", @@ -22,10 +22,10 @@ "pg-hstore": "^2.3.4", "sequelize": "^6.37.3", "sqlite3": "^5.1.7", + "stripe": "^15.12.0", "swagger-jsdoc": "^6.2.8", "swagger-ui-express": "^4.6.2", - "uuid": "^9.0.0", - "stripe": "^15.12.0" + "uuid": "^9.0.0" }, "devDependencies": { "eslint": "^9.5.0", diff --git a/frontend/.eslintrc.cjs b/frontend/.eslintrc.cjs index 3e212e1..9f9ce34 100644 --- a/frontend/.eslintrc.cjs +++ b/frontend/.eslintrc.cjs @@ -1,6 +1,6 @@ module.exports = { root: true, - env: { browser: true, es2020: true }, + env: { browser: true, es2020: true, node: true}, extends: [ 'eslint:recommended', 'plugin:react/recommended', diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 7e1bf87..c827eee 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -11,6 +11,7 @@ "@stripe/react-stripe-js": "^2.7.1", "bcrypt": "^5.1.1", "date-fns": "^3.6.0", + "dotenv": "^16.4.5", "express": "^4.19.2", "primeflex": "^3.3.1", "primeicons": "^7.0.0", @@ -2207,6 +2208,17 @@ "csstype": "^3.0.2" } }, + "node_modules/dotenv": { + "version": "16.4.5", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", + "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", diff --git a/frontend/package.json b/frontend/package.json index 4175da3..a532959 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -10,6 +10,7 @@ "@stripe/react-stripe-js": "^2.7.1", "bcrypt": "^5.1.1", "date-fns": "^3.6.0", + "dotenv": "^16.4.5", "express": "^4.19.2", "primeflex": "^3.3.1", "primeicons": "^7.0.0", diff --git a/frontend/src/components/forms/login/Login.jsx b/frontend/src/components/forms/login/Login.jsx index a9865cf..3bdb88a 100644 --- a/frontend/src/components/forms/login/Login.jsx +++ b/frontend/src/components/forms/login/Login.jsx @@ -12,8 +12,7 @@ export default function Login() { const navigate = useNavigate() const [user, setUser] = useState("") const [password, setPassword] = useState("") - // const domain = 'http://localhost:5000' - const domain = "http://86.38.204.61" + const domain = import.meta.env.VITE_API_URL function validateEmail(email) { const re = /^[^\s@]+@[^\s@]+\.[^\s@]+$/ @@ -27,7 +26,10 @@ export default function Login() { headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({user, password}) + body: JSON.stringify({ + user: user, + password: btoa(password) + }) }) .then(async response => { if (!response.ok) { @@ -36,6 +38,7 @@ export default function Login() { return response.json() }) .then(data => { + data.password = btoa(password) startSession(data) navigate("/virtual-dojo/frontend/dashboard") }) diff --git a/frontend/src/components/forms/meet/MeetRegisterForm.jsx b/frontend/src/components/forms/meet/MeetRegisterForm.jsx index a55270c..a581ae7 100644 --- a/frontend/src/components/forms/meet/MeetRegisterForm.jsx +++ b/frontend/src/components/forms/meet/MeetRegisterForm.jsx @@ -3,6 +3,7 @@ import { Button } from "primereact/button"; import { useState } from "react"; import { Calendar } from "primereact/calendar"; import styled from 'styled-components'; +import {getBase64CredentialsFromSession} from "../../../utils/session"; const RegisterContainer = styled.div` background-image: radial-gradient(circle at left top, var(--primary-400), var(--primary-700)); @@ -56,14 +57,15 @@ export default function MeetRegisterForm() { const [date, setDate] = useState(today); const [url, setUrl] = useState(''); const [price, setPrice] = useState(0); - // const domain = "http://localhost:5000" - const domain = "http://86.38.204.61"; + const domain = import.meta.env.VITE_API_URL + const base64Credentials = getBase64CredentialsFromSession() function saveMeet() { fetch(domain + '/api/meets', { method: 'POST', headers: { - 'Content-Type': 'application/json' + 'Content-Type': 'application/json', + 'Authorization': `Basic ${base64Credentials}` }, body: JSON.stringify({ meetUrl: url, meetDate: date, price: price }) }) diff --git a/frontend/src/components/forms/register/Register.jsx b/frontend/src/components/forms/register/Register.jsx index 75e234a..8818eed 100644 --- a/frontend/src/components/forms/register/Register.jsx +++ b/frontend/src/components/forms/register/Register.jsx @@ -14,8 +14,7 @@ export default function Register() { const [confirmPassword, setConfirmPassword] = useState("") const [errors, setErrors] = useState({}) const navigate = useNavigate() - // const domain = 'http://localhost:5000' - const domain = "http://86.38.204.61" + const domain = import.meta.env.VITE_API_URL const today = new Date() const majorityAgeDate = new Date(today.getFullYear() - 18, 0, 1) diff --git a/frontend/src/components/payment/CheckoutForm.jsx b/frontend/src/components/payment/CheckoutForm.jsx index 024a552..e696e84 100644 --- a/frontend/src/components/payment/CheckoutForm.jsx +++ b/frontend/src/components/payment/CheckoutForm.jsx @@ -1,15 +1,15 @@ import {useCallback} from "react" import {loadStripe} from '@stripe/stripe-js' import {format} from "date-fns" -import {getSession} from "../../utils/session.jsx"; +import {getBase64CredentialsFromSession, getSession} from "../../utils/session.jsx"; // eslint-disable-next-line react/prop-types export default function CheckoutForm({meet}) { - // const domain = "http://localhost:5000" - const domain = "http://86.38.204.61" + const domain = import.meta.env.VITE_API_URL const stripePromise = loadStripe("pk_test_51PSHknKnVUk9u0R7xWznb2PU2LeYeOgFXDVB14wP4BvJQBJ3RdH0ZLF801Ka7oLlNd7pFV7VZndQa2soCDluMFf200UugFXgnD") const user = getSession() + const base64Credentials = getBase64CredentialsFromSession() const fetchSessionId = useCallback(() => { return fetch(domain + "/api/stripe/create-checkout-session", { @@ -21,6 +21,7 @@ export default function CheckoutForm({meet}) { }), headers: { "Content-Type": "application/json", + 'Authorization': `Basic ${base64Credentials}` }, }) .then((res) => { diff --git a/frontend/src/hooks/useFetchMeets.js b/frontend/src/hooks/useFetchMeets.js index 2cbe8e1..b4d1094 100644 --- a/frontend/src/hooks/useFetchMeets.js +++ b/frontend/src/hooks/useFetchMeets.js @@ -1,15 +1,20 @@ import {useEffect, useState} from 'react'; +import {getBase64CredentialsFromSession} from "../utils/session"; function useFetchMeets(isUser, hasSession) { const [meets, setMeets] = useState([]); const [error, setError] = useState(null); - // const domain = 'http://localhost:5000' - const domain = 'http://86.38.204.61' + const domain = import.meta.env.VITE_API_URL + const base64Credentials = getBase64CredentialsFromSession() useEffect(() => { async function fetchMeets() { try { - const response = await fetch(domain + '/api/meets/all'); + const response = await fetch(domain + '/api/meets/all', { + headers: { + 'Authorization': `Basic ${base64Credentials}` + } + }); if (!response.ok) { console.error('Network response was not ok', error); diff --git a/frontend/src/hooks/useSavePurchase.js b/frontend/src/hooks/useSavePurchase.js index f5fb9e7..09fa48c 100644 --- a/frontend/src/hooks/useSavePurchase.js +++ b/frontend/src/hooks/useSavePurchase.js @@ -1,17 +1,21 @@ import {useEffect, useState} from "react" +import {getBase64CredentialsFromSession} from "../utils/session"; function useSavePurchase(isSuccess, meetId, userId) { const [response, setResponse] = useState({}) const [error, setError] = useState(null); - // const domain = 'http://localhost:5000' - const domain = 'http://86.38.204.61' + const domain = import.meta.env.VITE_API_URL + const base64Credentials = getBase64CredentialsFromSession() + useEffect(() => { async function savePurchase() { try { const response = await fetch(domain + '/api/purchases', { + method: 'POST', headers: { 'Content-Type': 'application/json', + 'Authorization': `Basic ${base64Credentials}` }, body: JSON.stringify({ meetId, userId}), }) diff --git a/frontend/src/utils/session.jsx b/frontend/src/utils/session.jsx index 5b63154..d9af1af 100644 --- a/frontend/src/utils/session.jsx +++ b/frontend/src/utils/session.jsx @@ -40,3 +40,8 @@ export const isAdmin = () => { const sessionData = getSession() return sessionData && sessionData.role && sessionData.role === 'ADMIN' } + +export const getBase64CredentialsFromSession = () => { + const sessionData = getSession() + return btoa(sessionData.email + ':' + sessionData.password) +} \ No newline at end of file