diff --git a/docker-compose.yml b/docker-compose.yml index 0327967..427b7f8 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -32,6 +32,10 @@ services: image: ghcr.io/arquisoft/wiq_7/userservice:latest profiles: ['dev', 'prod'] build: ./users/userservice + volumes: + - ./errors:/usr/src/userservice/errors + - ./middleware:/usr/src/userservice/middleware + - ./utils:/usr/src/userservice/utils depends_on: - mongodb ports: diff --git a/gatewayservice/gateway-service.js b/gatewayservice/gateway-service.js index 1d107dc..8c537f9 100644 --- a/gatewayservice/gateway-service.js +++ b/gatewayservice/gateway-service.js @@ -65,7 +65,7 @@ app.post('/login', async (req, res) => { app.get('/logout', async (req, res) => { try { // Forward the login request to the authentication service - const authResponse = await axios.get(authServiceUrl + '/logout', req.body); + const authResponse = await axios.get(authServiceUrl + '/logout'); res.json(authResponse.data); } catch (error) { res @@ -90,10 +90,25 @@ app.post('/adduser', async (req, res) => { }); app.get('/users', async (req, res) => { - console.log(req); try { // Forward the get users request to the user service - const userResponse = await axios.get(userServiceUrl + '/users', req.body); + const userResponse = await axios.get(userServiceUrl + '/users'); + res.json(userResponse.data); + } catch (error) { + res + .status(error.response.status) + .json({ error: error.response.data.error }); + } +}); + +app.get('/current-user', async (req, res) => { + try { + // Forward the get users request to the user service + const userResponse = await axios.get(userServiceUrl + '/current-user', { + headers: { + Authorization: req.headers.authorization, + }, + }); res.json(userResponse.data); } catch (error) { res @@ -126,8 +141,7 @@ app.get('/questions', async (req, res) => { try { // Forward the get question request to the question asking service const getQuestionResponse = await axios.get( - questionServiceUrl + '/questions', - req.body + questionServiceUrl + '/questions' ); res.json(getQuestionResponse.data); } catch (error) { @@ -179,10 +193,7 @@ app.post('/addstat', async (req, res) => { app.get('/stats', async (req, res) => { try { // Forward the get stats request to the stat service - const getStatsResponse = await axios.get( - statServiceUrl + '/stats', - req.body - ); + const getStatsResponse = await axios.get(statServiceUrl + '/stats'); res.json(getStatsResponse.data); } catch (error) { res diff --git a/middleware/auth-middleware.js b/middleware/auth-middleware.js index e82b9d4..cd777f0 100644 --- a/middleware/auth-middleware.js +++ b/middleware/auth-middleware.js @@ -12,6 +12,10 @@ export const authenticateUser = (req, res, next) => { const { userId, role } = verifyJWT(token); req.user = { userId, role }; + + console.log('auth mw req.user'); + console.log(req.user); + next(); } catch (error) { throw new UnauthenticatedError('Authentication invalid'); diff --git a/users/userservice/package-lock.json b/users/userservice/package-lock.json index f21b26c..0c0c204 100644 --- a/users/userservice/package-lock.json +++ b/users/userservice/package-lock.json @@ -12,6 +12,8 @@ "bcrypt": "^5.1.1", "body-parser": "^1.20.2", "express": "^4.18.2", + "http-status-codes": "^2.3.0", + "jsonwebtoken": "^9.0.2", "mongoose": "^8.0.4" }, "devDependencies": { @@ -1656,6 +1658,12 @@ "node": "*" } }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", + "license": "BSD-3-Clause" + }, "node_modules/buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", @@ -2061,6 +2069,15 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -2690,6 +2707,12 @@ "node": ">= 0.8" } }, + "node_modules/http-status-codes": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/http-status-codes/-/http-status-codes-2.3.0.tgz", + "integrity": "sha512-RJ8XvFvpPM/Dmc5SV+dC4y5PCeOhT3x1Hq0NU3rjGeg5a/CqlhZ7uudknPwZFz4aeAXDcbAyaeP7GAo9lvngtA==", + "license": "MIT" + }, "node_modules/https-proxy-agent": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", @@ -3602,6 +3625,55 @@ "node": ">=6" } }, + "node_modules/jsonwebtoken": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", + "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", + "license": "MIT", + "dependencies": { + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + } + }, + "node_modules/jsonwebtoken/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/jwa": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", + "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", + "license": "MIT", + "dependencies": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "license": "MIT", + "dependencies": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, "node_modules/kareem": { "version": "2.5.1", "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.5.1.tgz", @@ -3646,6 +3718,48 @@ "node": ">=8" } }, + "node_modules/lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==", + "license": "MIT" + }, + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==", + "license": "MIT" + }, + "node_modules/lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==", + "license": "MIT" + }, + "node_modules/lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==", + "license": "MIT" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "license": "MIT" + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", + "license": "MIT" + }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", + "license": "MIT" + }, "node_modules/lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", diff --git a/users/userservice/package.json b/users/userservice/package.json index f02e73b..ebd0271 100644 --- a/users/userservice/package.json +++ b/users/userservice/package.json @@ -25,6 +25,8 @@ "bcrypt": "^5.1.1", "body-parser": "^1.20.2", "express": "^4.18.2", + "http-status-codes": "^2.3.0", + "jsonwebtoken": "^9.0.2", "mongoose": "^8.0.4" }, "devDependencies": { diff --git a/users/userservice/user-controller.js b/users/userservice/user-controller.js index 7c3e441..0989ce6 100644 --- a/users/userservice/user-controller.js +++ b/users/userservice/user-controller.js @@ -46,9 +46,9 @@ export const getUsersController = async (req, res) => { export const getCurrentUserController = async (req, res) => { try { + console.log(req.user.userId); const user = await User.findOne({ _id: req.user.userId }); // Fetch current user - const userWithoutPassword = user.toJSON; - res.json(userWithoutPassword); + res.json({ user: user }); } catch (error) { res.status(500).json({ error: error.message }); } diff --git a/users/userservice/user-router.js b/users/userservice/user-router.js index e595d33..151d49a 100644 --- a/users/userservice/user-router.js +++ b/users/userservice/user-router.js @@ -1,11 +1,17 @@ // userRouter.js import express from 'express'; -import { addUserController, getUsersController } from './user-controller.js'; +import { + addUserController, + getUsersController, + getCurrentUserController, +} from './user-controller.js'; +import { authenticateUser } from './middleware/auth-middleware.js'; const userRouter = express.Router(); // Define la ruta para el login y asocia el controlador userRouter.post('/adduser', addUserController); userRouter.get('/users', getUsersController); +userRouter.get('/current-user', authenticateUser, getCurrentUserController); export default userRouter; diff --git a/utils/tokenUtils.js b/utils/tokenUtils.js index f7d84dc..bd5aa8d 100644 --- a/utils/tokenUtils.js +++ b/utils/tokenUtils.js @@ -9,8 +9,6 @@ export const createJWT = (payload) => { }; export const verifyJWT = (token) => { - console.log('verjwt'); - console.log(token); const decoded = jwt.verify(token, 'your-secret-key'); // process.env.JWT_SECRET); return decoded; }; diff --git a/webapp/src/App.jsx b/webapp/src/App.jsx index a7798b2..3ddf71c 100644 --- a/webapp/src/App.jsx +++ b/webapp/src/App.jsx @@ -13,6 +13,9 @@ import { Admin, } from './pages'; +import { loader as dashboardLoader } from './pages/DashboardLayout'; +import { loader as adminLoader } from './pages/Admin'; + export const checkDefaultTheme = () => { const isDarkTheme = localStorage.getItem('darkTheme') === 'true'; document.body.classList.toggle('dark-theme', isDarkTheme); @@ -42,6 +45,7 @@ const router = createBrowserRouter([ { path: 'dashboard', element: , + loader: dashboardLoader, children: [ { index: true, @@ -62,6 +66,7 @@ const router = createBrowserRouter([ { path: 'admin', element: , + loader: adminLoader, }, ], }, diff --git a/webapp/src/assets/wrappers/AddQuestionContainer.js b/webapp/src/assets/wrappers/AddQuestionContainer.js index 9219500..cb6fa86 100644 --- a/webapp/src/assets/wrappers/AddQuestionContainer.js +++ b/webapp/src/assets/wrappers/AddQuestionContainer.js @@ -1,8 +1,16 @@ import styled from 'styled-components'; const Wrapper = styled.section` + background: var(--background-secondary-color); + border-radius: var(--border-radius); display: grid; + grid-template-rows: 1fr auto; + grid-template-columns: repeat(3, 1fr); row-gap: 2rem; + column-gap: 2rem; + + margin: 0.5rem auto; + padding: 1rem; @media (min-width: 768px) { grid-template-columns: 1fr 1fr; column-gap: 1rem; diff --git a/webapp/src/components/LogoutContainer.jsx b/webapp/src/components/LogoutContainer.jsx index 816ad2e..72fec10 100644 --- a/webapp/src/components/LogoutContainer.jsx +++ b/webapp/src/components/LogoutContainer.jsx @@ -15,7 +15,7 @@ const LogoutContainer = () => { onClick={() => setShowLogout(!showLogout)} > - {user?.name} + {user?.username}
diff --git a/webapp/src/pages/AddUser.jsx b/webapp/src/pages/AddUser.jsx index 4c23370..a0df33b 100644 --- a/webapp/src/pages/AddUser.jsx +++ b/webapp/src/pages/AddUser.jsx @@ -31,7 +31,7 @@ const AddUser = () => { // Añadir un retardo antes de navegar setTimeout(() => { navigate('/login'); - }, 2000); // 2000 ms = 2 segundos + }, 1000); // 1000 ms = 1 segundos } catch (error) { setError(error.response?.data?.error || 'An error occurred'); } diff --git a/webapp/src/pages/Admin.jsx b/webapp/src/pages/Admin.jsx index 50894cc..2193adc 100644 --- a/webapp/src/pages/Admin.jsx +++ b/webapp/src/pages/Admin.jsx @@ -1,6 +1,30 @@ +import { useLoaderData, redirect } from 'react-router-dom'; +import axios from 'axios'; +import Wrapper from '../assets/wrappers/StatsContainer'; import { AddQuestionContainer } from '../components'; +const apiEndpoint = + process.env.REACT_APP_API_ENDPOINT || 'http://localhost:8000'; + +export const loader = async () => { + try { + const token = localStorage.getItem('token'); + const response = await axios.get(`${apiEndpoint}/current-user`, { + headers: { + Authorization: `Bearer ${token}`, + }, + }); + + return response.data; + } catch (error) { + console.log('You are not authorized to view this page'); + return redirect('/dashboard'); + } +}; + const Admin = () => { + const { user } = useLoaderData(); + return ; }; diff --git a/webapp/src/pages/DashboardLayout.jsx b/webapp/src/pages/DashboardLayout.jsx index c368c95..7cbbac6 100644 --- a/webapp/src/pages/DashboardLayout.jsx +++ b/webapp/src/pages/DashboardLayout.jsx @@ -1,4 +1,4 @@ -import { Outlet, useNavigate } from 'react-router-dom'; +import { Outlet, redirect, useLoaderData, useNavigate } from 'react-router-dom'; import Wrapper from '../assets/wrappers/Dashboard'; import { SmallSidebar, BigSidebar, Navbar } from '../components'; import { createContext, useContext, useState } from 'react'; @@ -9,18 +9,30 @@ import { Snackbar } from '@mui/material'; const apiEndpoint = process.env.REACT_APP_API_ENDPOINT || 'http://localhost:8000'; +export const loader = async () => { + try { + const token = localStorage.getItem('token'); + const response = await axios.get(`${apiEndpoint}/current-user`, { + headers: { + Authorization: `Bearer ${token}`, + }, + }); + return response.data; + } catch (error) { + return redirect('/'); + } +}; + const DashboardContext = createContext(); const DashboardLayout = () => { - // temp - const user = { name: 'user' }; + const { user } = useLoaderData(); + const navigate = useNavigate(); const [error, setError] = useState(''); const [openSnackbar, setOpenSnackbar] = useState(false); const [showSidebar, setShowSidebar] = useState(false); const [isDarkTheme, setIsDarkTheme] = useState(checkDefaultTheme()); - const navigate = useNavigate(); - const toggleDarkTheme = () => { const newDarkTheme = !isDarkTheme; setIsDarkTheme(newDarkTheme); @@ -40,9 +52,7 @@ const DashboardLayout = () => { Authorization: `Bearer ${token}`, }, }); - console.log('token'); localStorage.removeItem('token'); - console.log('token'); setOpenSnackbar(true); // Añadir un retardo antes de navegar setTimeout(() => { diff --git a/webapp/src/pages/Landing.jsx b/webapp/src/pages/Landing.jsx index 9e5b813..f743582 100644 --- a/webapp/src/pages/Landing.jsx +++ b/webapp/src/pages/Landing.jsx @@ -15,10 +15,9 @@ const Landing = () => { wiq quiz game

- Lorem ipsum dolor sit amet consectetur adipisicing elit. Tenetur - quas quidem itaque doloremque accusantium laudantium? Earum maiores - consectetur vero eligendi, beatae reprehenderit recusandae placeat - accusantium dignissimos deserunt sed quasi quisquam! + Sumérgete en la emoción del concurso, ahora en versión digital. + Desafía tus conocimientos, compite con amigos y demuestra tu + sabiduría en esta experiencia interactiva única.

Register diff --git a/webapp/src/pages/Login.jsx b/webapp/src/pages/Login.jsx index 9b445bc..a292522 100644 --- a/webapp/src/pages/Login.jsx +++ b/webapp/src/pages/Login.jsx @@ -33,7 +33,7 @@ const Login = () => { // Añadir un retardo antes de navegar setTimeout(() => { navigate('/dashboard'); - }, 2000); // 2000 ms = 2 segundos + }, 1000); // 1000 ms = 1 segundos } catch (error) { setError(error.response.data.error); }