diff --git a/cosiap_api/cosiap_api/settings.py b/cosiap_api/cosiap_api/settings.py index ea7b0b0819ef9346d07278f4062654f61c2d357e..8b3015dc827cbb070efe3d5dc500301ecfbe3358 100644 --- a/cosiap_api/cosiap_api/settings.py +++ b/cosiap_api/cosiap_api/settings.py @@ -11,6 +11,7 @@ https://docs.djangoproject.com/en/5.0/ref/settings/ """ import os +from datetime import timedelta # Build paths inside the project like this: BASE_DIR / 'subdir'. BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) @@ -48,7 +49,7 @@ INSTALLED_APPS = [ 'users', ] -MIDDLEWARE = [ +MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'corsheaders.middleware.CorsMiddleware', @@ -172,6 +173,32 @@ REST_FRAMEWORK = { 'rest_framework_simplejwt.authentication.JWTAuthentication', ), 'DEFAULT_SCHEMA_CLASS': 'drf_spectacular.openapi.AutoSchema', + 'DEFAULT_PERMISSION_CLASSES': [ + 'rest_framework.permissions.IsAuthenticated' + #'rest_framework.permissions.AllowAny' + ], +} + +CSRF_COOKIE_SECURE = True +SESSION_COOKIE_SECURE = True +SESSION_COOKIE_HTTPONLY = True + +SIMPLE_JWT = { + 'ACCESS_TOKEN_LIFETIME': timedelta(minutes=5), + 'REFRESH_TOKEN_LIFETIME': timedelta(days=1), + 'ROTATE_REFRESH_TOKENS': False, + 'BLACKLIST_AFTER_ROTATION': False, + 'ALGORITHM': 'HS256', + 'SIGNING_KEY': SECRET_KEY, + 'VERIFYING_KEY': None, + 'AUDIENCE': None, + 'ISSUER': None, + 'AUTH_HEADER_TYPES': ('Bearer',), + 'USER_ID_FIELD': 'id', + 'USER_ID_CLAIM': 'user_id', + 'AUTH_TOKEN_CLASSES': ('rest_framework_simplejwt.tokens.AccessToken',), + 'TOKEN_TYPE_CLAIM': 'token_type', + 'JTI_CLAIM': 'jti', } SPECTACULAR_SETTINGS = { @@ -182,7 +209,7 @@ SPECTACULAR_SETTINGS = { # OTHER SETTINGS } -#CORS_ALLOWED_ORIGINS = [ -# "http://localhost:5173" -#] - +CORS_ALLOW_CREDENTIALS = True +CORS_ALLOWED_ORIGINS = [ + "http://localhost:5173" +] diff --git a/cosiap_api/cosiap_api/urls.py b/cosiap_api/cosiap_api/urls.py index 2ac902b84738cde32269c42061e7da9b4a446561..7d69109e2d890b25f7ea4e52f778f69cab8ec371 100644 --- a/cosiap_api/cosiap_api/urls.py +++ b/cosiap_api/cosiap_api/urls.py @@ -18,20 +18,14 @@ from django.contrib import admin from django.conf import settings from django.conf.urls.static import static from django.urls import path, include -from rest_framework_simplejwt.views import ( - TokenObtainPairView, - TokenRefreshView, -) from drf_spectacular.views import SpectacularAPIView, SpectacularRedocView, SpectacularSwaggerView urlpatterns = [ path('admin/', admin.site.urls), - path('',include('users.urls')), - - path('api/token/', TokenObtainPairView.as_view(), name='token_obtain_pair'), - path('api/token/refresh/', TokenRefreshView.as_view(), name='token_refresh'), + path('api/usuarios/',include('users.urls')), # API Doc UI: + path('api/schema/', SpectacularAPIView.as_view(), name='schema'), path('api/schema/swagger-ui/', SpectacularSwaggerView.as_view(url_name='schema'), name='swagger-ui'), path('api/schema/redoc/', SpectacularRedocView.as_view(url_name='schema'), name='redoc'), ] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) \ No newline at end of file diff --git a/cosiap_api/users/urls.py b/cosiap_api/users/urls.py index 64b4312cdd551511cd48e5416b7c4a68d6a96367..5d0247cc9fd8f868a1e28e2b0f14d36a7125fcd4 100644 --- a/cosiap_api/users/urls.py +++ b/cosiap_api/users/urls.py @@ -16,9 +16,14 @@ Including another URLconf from . import views from django.urls import path from django.contrib.auth import views as auth_views +from .views import CustomTokenObtainPairView, CustomTokenRefreshView + app_name = 'users' -urlpatterns = [ +urlpatterns = [ + path('token/', CustomTokenObtainPairView.as_view(), name='token_obtain_pair'), + path('token/refresh/', CustomTokenRefreshView.as_view(), name='token_refresh'), + path('usuarios/', views.usuario.as_view(), name = 'usuario-lista-crear'), path('usuarios//', views.usuario.as_view(), name = 'ver-eliminar-usuario'), path('usuarios/verificar///', views.verificar_token, name='verificar_token'), diff --git a/cosiap_api/users/views.py b/cosiap_api/users/views.py index cbac2c3f53cf7299ae7aa632e7187cf40456a3d3..b83d10490252659f65edf8aeb7596836b33f43bc 100644 --- a/cosiap_api/users/views.py +++ b/cosiap_api/users/views.py @@ -1,5 +1,5 @@ # Archivo con la funcionalidad necesaria para la gestión de usuarios en la API -# Autores: Adalberto Cerrillo Vázquez, +# Autores: Adalberto Cerrillo Vázquez, Rafael Uribe Caldera # Versión: 1.0 from django.shortcuts import render, get_object_or_404, redirect @@ -17,6 +17,11 @@ from django.utils.http import urlsafe_base64_encode, urlsafe_base64_decode from django.utils.encoding import force_bytes, force_str from django.contrib.auth.tokens import PasswordResetTokenGenerator from .tokens import account_activation_token +from rest_framework_simplejwt.views import TokenObtainPairView, TokenRefreshView +from rest_framework.response import Response +from rest_framework import status +from django.conf import settings +from datetime import datetime, timedelta # Funcionalidad para verificar que el usuario que realiza la eliminación sea un admin class es_admin(permissions.BasePermission): @@ -24,6 +29,33 @@ class es_admin(permissions.BasePermission): def has_permission( self, request, view): return request.user and request.user.is_superuser +class CustomTokenObtainPairView(TokenObtainPairView): + def post(self, request, *args, **kwargs): + response = super().post(request, *args, **kwargs) + if response.status_code == status.HTTP_200_OK: + refresh_token = response.data['refresh'] + response.set_cookie( + key='refresh_token', + value=refresh_token, + httponly=True, + secure=True, + samesite='Strict', # Whether to set the flag restricting cookie leaks on cross-site requests. This can be 'Lax', 'Strict', or None to disable the flag. + expires=datetime.now() + settings.SIMPLE_JWT['REFRESH_TOKEN_LIFETIME'], + ) + # Eliminar el refresh token de la respuesta JSON + del response.data['refresh'] + return response + +class CustomTokenRefreshView(TokenRefreshView): + def post(self, request, *args, **kwargs): + refresh_token = request.COOKIES.get('refresh_token') + if not refresh_token: + return Response({"detail": "Refresh token is missing."}, status=status.HTTP_400_BAD_REQUEST) + request.data['refresh'] = refresh_token + response = super().post(request, *args, **kwargs) + if response.status_code != status.HTTP_200_OK: + response.delete_cookie('refresh_token') + return response # Funcionalidad para crear un usuario en el sistema, ver sus datos o eliminarlo class usuario(APIView): @@ -168,4 +200,5 @@ def verificar_token(request, uidb64, token): # si algo sale mal, indicamos simplemente que el token no es válido messages.error(request, 'El enlace de verificación no es válido o ha expirado.') # NOTA ADAL: Esta redirección se cambiara según las necesidades futuras - return redirect('users:usuario-lista-crear') \ No newline at end of file + return redirect('users:usuario-lista-crear') +>>>>>>> cosiap_api/users/views.py diff --git a/cosiap_frontend/Dockerfile b/cosiap_frontend/Dockerfile index cd81c8f9638496020fa4ea06b60eefbb6470b20d..0b9adf91d2f0fc857043662acc1359d66e636550 100644 --- a/cosiap_frontend/Dockerfile +++ b/cosiap_frontend/Dockerfile @@ -4,7 +4,7 @@ WORKDIR /app COPY ./package.json . COPY ./package-lock.json . -RUN npm install +#RUN npm install COPY . . diff --git a/cosiap_frontend/docker-compose.yaml b/cosiap_frontend/docker-compose.yaml index 7b14af4d6394c61f758f2f47c19131aa033d9fe5..ab8a9ffebc3dd3fa2ad06171a166f11e1bb459f0 100644 --- a/cosiap_frontend/docker-compose.yaml +++ b/cosiap_frontend/docker-compose.yaml @@ -8,7 +8,7 @@ services: context: . volumes: - .:/app - - /app/node_modules + #- /app/node_modules environment: - CHOKIDAR_USEPOLLING=true ports: diff --git a/cosiap_frontend/entrypoint.sh b/cosiap_frontend/entrypoint.sh index c77204c81d006ec66c8f38747d1b8a08e5d47a08..769092689322299ed7bfb0542be6b995ec4b6a16 100644 --- a/cosiap_frontend/entrypoint.sh +++ b/cosiap_frontend/entrypoint.sh @@ -1,5 +1,7 @@ #!/bin/sh -npm run dev-exposed & -npx tailwindcss -i ./src/input.css -o ./src/output.css --watch +echo "RUN npm install" +npm install --verbose +npm run dev-exposed & +npx tailwindcss -i ./src/input.css -o ./src/output.css --watch \ No newline at end of file diff --git a/cosiap_frontend/package-lock.json b/cosiap_frontend/package-lock.json index 0f91ceffb913f25eb0d934e2769f3b7ceae415e1..445cb0a55447b59d442bd94f016e0c475940bfeb 100644 --- a/cosiap_frontend/package-lock.json +++ b/cosiap_frontend/package-lock.json @@ -8,6 +8,7 @@ "name": "cosiap_frontend", "version": "0.0.0", "dependencies": { + "axios": "^1.7.2", "react": "^18.2.0", "react-dom": "^18.2.0", "react-helmet-async": "^2.0.5", @@ -1359,6 +1360,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, "node_modules/available-typed-arrays": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", @@ -1374,6 +1381,17 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/axios": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.2.tgz", + "integrity": "sha512-2A8QhOMrbomlDuiLeK9XibIBzuHeRcqqNOHp0Cyp5EoJ1IFDh+XZH3A6BkXtv0K4gFGCI0Y4BM7B1wOEi0Rmgw==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -1526,6 +1544,18 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/commander": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", @@ -1683,6 +1713,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/didyoumean": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", @@ -2256,6 +2295,26 @@ "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", "dev": true }, + "node_modules/follow-redirects": { + "version": "1.15.6", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", + "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, "node_modules/for-each": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", @@ -2282,6 +2341,20 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -3190,6 +3263,27 @@ "node": ">=8.6" } }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -3752,6 +3846,12 @@ "react-is": "^16.13.1" } }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "license": "MIT" + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", diff --git a/cosiap_frontend/package.json b/cosiap_frontend/package.json index e75ab700bdc535ddaff9838323781af68efc2af2..b2f90c01ef5ed273149e754745356d48fa7e0ba7 100644 --- a/cosiap_frontend/package.json +++ b/cosiap_frontend/package.json @@ -11,6 +11,7 @@ "preview": "vite preview" }, "dependencies": { + "axios": "^1.7.2", "react": "^18.2.0", "react-dom": "^18.2.0", "react-helmet-async": "^2.0.5", diff --git a/cosiap_frontend/src/App.jsx b/cosiap_frontend/src/App.jsx index 747e3dac48711cadc7f9660aa661c163684e11b4..113ebfb3530ba5834827ad4bdb131f9f7cac68aa 100644 --- a/cosiap_frontend/src/App.jsx +++ b/cosiap_frontend/src/App.jsx @@ -1,8 +1,12 @@ import React, { useEffect, useState } from "react"; import { BrowserRouter, Routes, Route, Navigate } from 'react-router-dom'; -import {LoginPage} from "./users/loginPage" -import {PaginaHead} from "./common/PaginaHead" -import "./App.css" +import {PaginaHead} from "./common/PaginaHead"; +import {Autenticador} from "./users/Autenticador"; +import {LoginRequiredRoutes} from "./common/LoginRequiredRoutes" +import "./App.css"; + +import {LoginPage} from "./users/loginPage"; +import {InicioTest} from './users/InicioTest'; function App() { return ( @@ -12,10 +16,17 @@ function App() { - - } /> - } /> - + + + } /> + } /> + {/* Rutas protegidas */} + }> + } /> + + + + ); } diff --git a/cosiap_frontend/src/api.js b/cosiap_frontend/src/api.js new file mode 100644 index 0000000000000000000000000000000000000000..8e3f03f616bbc98ebaf5f7e491a232dd7a9fc7fd --- /dev/null +++ b/cosiap_frontend/src/api.js @@ -0,0 +1,43 @@ +import axios from "axios"; +axios.defaults.withCredentials = true; + +const apiUrl = "http://localhost:8000"; + + +// Configuración de Axios +const api = axios.create({ + baseURL: apiUrl, +}); + +// Interceptor de solicitud +api.interceptors.request.use( + (config) => { + // Aquí puedes manipular la configuración de la solicitud antes de enviarla al servidor + console.log('Solicitud enviada:', config); + // Por ejemplo, agregar encabezados comunes como token de autenticación + // config.headers['Authorization'] = `Bearer ${localStorage.getItem('token')}`; + return config; + }, + (error) => { + // Manejo de errores del interceptor de solicitud + console.log('Error en el interceptor de solicitud:', error); + return Promise.reject(error); + } +); + +// Interceptor de respuesta +api.interceptors.response.use( + (response) => { + // Aquí puedes manipular la respuesta antes de pasarla al código que la llamó + console.log('Respuesta recibida:', response); + return response; + }, + (error) => { + // Manejo de errores del interceptor de respuesta + console.log('Error en el interceptor de respuesta:', error); + return Promise.reject(error); + } +); + +// Exportar la instancia de Axios como tu API principal +export default api; \ No newline at end of file diff --git a/cosiap_frontend/src/common/LoginRequiredRoutes.jsx b/cosiap_frontend/src/common/LoginRequiredRoutes.jsx new file mode 100644 index 0000000000000000000000000000000000000000..f20b0646aa9307903a135d6f13dbc7d1d5030be9 --- /dev/null +++ b/cosiap_frontend/src/common/LoginRequiredRoutes.jsx @@ -0,0 +1,16 @@ +import { useAutenticacion } from "@/users/Autenticador"; +import { Navigate, Outlet } from "react-router-dom"; + +export const LoginRequiredRoutes = () => { + const { token } = useAutenticacion(); + + if (token === null) { + return ; + } else if (token === undefined) { + return null; // O un spinner de carga o marcador de posición + } else { + return ; + } +}; + +export default LoginRequiredRoutes; diff --git a/cosiap_frontend/src/main.jsx b/cosiap_frontend/src/main.jsx index 0b3166c01fe8775f7550cf08f0dbaf5c4c14cb57..e7f84ab6e09d971559309938fa83b6a5cef5acd9 100644 --- a/cosiap_frontend/src/main.jsx +++ b/cosiap_frontend/src/main.jsx @@ -5,7 +5,7 @@ import './index.css' import './output.css' ReactDOM.createRoot(document.getElementById('root')).render( - + // - , + //, ) diff --git a/cosiap_frontend/src/users/InicioTest.jsx b/cosiap_frontend/src/users/InicioTest.jsx new file mode 100644 index 0000000000000000000000000000000000000000..9b6e9e1b2c67147352215a24230b0ad14364817b --- /dev/null +++ b/cosiap_frontend/src/users/InicioTest.jsx @@ -0,0 +1,11 @@ +export const InicioTest = () => { + return ( +
+

Inicio

+

Inicio

+

Inicio

+

Inicio

+
Inicio
+
+ ); +} diff --git a/cosiap_frontend/src/users/autenticador.jsx b/cosiap_frontend/src/users/autenticador.jsx new file mode 100644 index 0000000000000000000000000000000000000000..cd9856b6ef9e99b79d45f225b6d0075ed613acd5 --- /dev/null +++ b/cosiap_frontend/src/users/autenticador.jsx @@ -0,0 +1,93 @@ +import { + useEffect, + useLayoutEffect, + useContext, + createContext, + useState, +} from "react"; +import api from '@/api' +import PropTypes from 'prop-types'; + +const ContextoAut = createContext(undefined); + +export const useAutenticacion = () => { + const contextoAut = useContext(ContextoAut); + + if (!contextoAut) { + throw new Error( + "useAutenticacion se tiene que usar dentro de un componente autenticador" + ); + } + + return contextoAut; +}; + +export const Autenticador = ({ children }) => { + console.log('INICIALIZANDO AUTENTICADOR') + const [token, setToken] = useState(); + + useEffect(() => { + const buscarUsuario = async () => { + console.log('setear session') + try { + const response = await api.post('api/usuarios/token/refresh/'); + setToken(response.data.access); + } catch { + setToken(null); + } + } + + buscarUsuario(); + }, []); + + useLayoutEffect(() => { + const interceptadorAccess = api.interceptors.request.use((config) => { + console.log(`insertar token en request: ${token}`) + config.headers.Authorization = !config.__retry && token ? `Bearer ${token}` : config.headers.Authorization; + return config; + }); + + return () => { + api.interceptors.request.eject(interceptadorAccess); + }; + + }, [token]); + + useLayoutEffect(() => { + const interceptadorRefresh = api.interceptors.response.use( + (response) => response, async (error) => { + const requestOriginal = error.config; + + console.log('error en la response'); + if (error.response.status === 403 && error.response.data.message === 'Unauthorized') { + try { + const response = await api.post('api/usuarios/token/refresh/'); + setToken(response.data.access); + requestOriginal.headers.Authorization = `Bearer ${response.data.access}`; + requestOriginal.__retry = true; + + return api(requestOriginal); + } catch { + setToken(null); + } + } + + return Promise.reject(error); + }, + ); + + return () => { + api.interceptors.response.eject(interceptadorRefresh); + }; + }); + return ( + + {console.log(token)} + {children} + + ); +}; + +Autenticador.propTypes = { + children: PropTypes.node, +}; \ No newline at end of file diff --git a/cosiap_frontend/src/users/loginPage.jsx b/cosiap_frontend/src/users/loginPage.jsx index c93c30d81c7cf47a4d4219d8a0269e5a6327e1e2..c9897ae2dd6b4c32d89ac1468e843bc1198ba225 100644 --- a/cosiap_frontend/src/users/loginPage.jsx +++ b/cosiap_frontend/src/users/loginPage.jsx @@ -1,8 +1,49 @@ +import { useState } from 'react'; +import api from '@/api'; -export function LoginPage(){ - return ( - <> - Login page - - ); -} \ No newline at end of file +export function LoginPage() { + const [curp, setCurp] = useState(''); + const [password, setPassword] = useState(''); + + const handleSubmit = async (event) => { + event.preventDefault(); + + try { + const response = await api.post('api/usuarios/token/', { curp, password }); + console.log('Respuesta del servidor:', response.data); + // Aquí puedes manejar la respuesta del servidor, por ejemplo, redirigir al usuario a otra página + } catch (error) { + console.error('Error al enviar la solicitud:', error); + // Aquí puedes manejar errores, como mostrar un mensaje al usuario + } + }; + + return ( +
+

Login Page

+
+ +
+ +
+ +
+
+ ); +} diff --git a/cosiap_frontend/vite.config.js b/cosiap_frontend/vite.config.js index fb14d26d256d4a25ee651d8c8dc200ea56b9e9d6..8847835442e0366880c9eb0ebfb89d6671cd045a 100644 --- a/cosiap_frontend/vite.config.js +++ b/cosiap_frontend/vite.config.js @@ -1,10 +1,16 @@ -import { defineConfig } from 'vite' -import react from '@vitejs/plugin-react-swc' +import { defineConfig } from 'vite'; +import react from '@vitejs/plugin-react-swc'; +import path from 'path'; // https://vitejs.dev/config/ export default defineConfig({ plugins: [react()], server: { host: '0.0.0.0' - } + }, + resolve: { + alias: { + "@": path.resolve(__dirname, "./src"), + }, + }, }) diff --git a/docker-compose.yaml b/docker-compose.yaml index 5bbd3f878f95fefcfbf470636e5226a2d9824934..5020b920d627e231881915418f8d4c3acc1337dd 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -44,7 +44,7 @@ services: context: ./cosiap_frontend volumes: - ./cosiap_frontend:/app - - /app/node_modules + #- /app/node_modules environment: - CHOKIDAR_USEPOLLING=true ports: