fix: arreglar conexión con google auth y proceso de arreglar conexión entre paso2 y paso3

This commit is contained in:
Alexis
2026-04-14 10:29:25 +02:00
parent cb9030eb8d
commit f7bfc4b956
5 changed files with 83 additions and 41 deletions
+1 -1
View File
@@ -35,7 +35,7 @@ class DoCIT2MFRequest(BaseModel):
def support_valid(cls, v, info): def support_valid(cls, v, info):
c, d = v c, d = v
if c >= d: if c >= d:
raise ValueError("El soporte debe cumplir c < d.") raise ValueError("El soporte debe cumplir c <= d.")
core = info.data.get("core") core = info.data.get("core")
if core: if core:
+4 -4
View File
@@ -1,7 +1,7 @@
# api/routers/docit2mf_build.py # api/routers/docit2mf_build.py
import logging import logging
from fastapi import APIRouter, HTTPException from fastapi import APIRouter, HTTPException, Request
from slowapi import Limiter from slowapi import Limiter
from slowapi.util import get_remote_address from slowapi.util import get_remote_address
from api.models.docit2mf_models import DoCIT2MFMultiRequest from api.models.docit2mf_models import DoCIT2MFMultiRequest
@@ -14,11 +14,11 @@ logger = logging.getLogger(__name__)
@router.post("/doc-it2mf/build") @router.post("/doc-it2mf/build")
@limiter.limit("10/minute") @limiter.limit("10/minute")
async def build_doc_it2mf(request: DoCIT2MFMultiRequest): async def build_doc_it2mf(request: Request, body: DoCIT2MFMultiRequest):
results = [] results = []
try: try:
for level in request.levels: for level in body.levels:
results.append(build_it2mf_from_level(level)) results.append(build_it2mf_from_level(level))
except ValueError as e: except ValueError as e:
logger.warning(f"Validation error in doc-it2mf/build: {str(e)}") logger.warning(f"Validation error in doc-it2mf/build: {str(e)}")
@@ -27,4 +27,4 @@ async def build_doc_it2mf(request: DoCIT2MFMultiRequest):
logger.error(f"Unexpected error in doc-it2mf/build: {str(e)}", exc_info=True) logger.error(f"Unexpected error in doc-it2mf/build: {str(e)}", exc_info=True)
raise HTTPException(status_code=500, detail="Internal server error") raise HTTPException(status_code=500, detail="Internal server error")
return {"levels": results} return {"levels": results}
+18 -5
View File
@@ -1,19 +1,18 @@
# api/routers/google_auth.py
from fastapi import APIRouter, HTTPException, Request from fastapi import APIRouter, HTTPException, Request
from fastapi.responses import RedirectResponse from fastapi.responses import RedirectResponse
from datetime import datetime, timedelta
import httpx import httpx
import os import os
import jwt import jwt
from api.database.mongodb import users_collection from api.database.mongodb import users_collection
from api.utils.security import create_access_token
router = APIRouter(prefix="/auth/google", tags=["auth"]) router = APIRouter(prefix="/auth/google", tags=["auth"])
GOOGLE_CLIENT_ID = os.getenv("GOOGLE_CLIENT_ID") GOOGLE_CLIENT_ID = os.getenv("GOOGLE_CLIENT_ID")
GOOGLE_CLIENT_SECRET = os.getenv("GOOGLE_CLIENT_SECRET") GOOGLE_CLIENT_SECRET = os.getenv("GOOGLE_CLIENT_SECRET")
REDIRECT_URI = os.getenv("GOOGLE_REDIRECT_URI") REDIRECT_URI = os.getenv("GOOGLE_REDIRECT_URI")
SECRET_KEY = os.getenv("SECRET_KEY")
# ----------------------------- # -----------------------------
@@ -90,6 +89,20 @@ async def google_callback(request: Request):
else: else:
user_id = user["_id"] user_id = user["_id"]
token = create_access_token({"user_id": str(user_id)}) token = jwt.encode(
{
"sub": str(user_id),
"email": email,
"name": name,
"exp": datetime.utcnow() + timedelta(hours=24)
},
SECRET_KEY,
algorithm="HS256"
)
return RedirectResponse(f"http://localhost:5173/login?token={token}") await users_collection.update_one(
{"_id": user_id},
{"$set": {"token": token}}
)
return RedirectResponse(f"http://localhost:5173/login?token={token}")
+20 -10
View File
@@ -1,23 +1,33 @@
import { useState } from 'react'; import { useState, useCallback } from 'react';
import { AuthContext } from './AuthContext'; import { AuthContext } from './AuthContext';
export const AuthProvider = ({ children }) => { export const AuthProvider = ({ children }) => {
const [user, setUser] = useState(() => { const [user, setUser] = useState(() => {
const storedUser = localStorage.getItem('user'); try {
return storedUser ? JSON.parse(storedUser) : null; const storedUser = localStorage.getItem('user');
return storedUser ? JSON.parse(storedUser) : null;
} catch {
return null;
}
}); });
const login = (userData, token) => { const login = useCallback((data) => {
setUser(userData); const currentUser = data.user || data;
localStorage.setItem('user', JSON.stringify(userData)); const token = data.access_token || data.token;
localStorage.setItem('token', token);
};
const logout = () => { setUser(currentUser);
localStorage.setItem('user', JSON.stringify(currentUser));
if (token) {
localStorage.setItem('token', token);
}
}, []);
const logout = useCallback(() => {
setUser(null); setUser(null);
localStorage.removeItem('user'); localStorage.removeItem('user');
localStorage.removeItem('token'); localStorage.removeItem('token');
}; }, []);
return ( return (
<AuthContext.Provider value={{ <AuthContext.Provider value={{
+40 -21
View File
@@ -7,9 +7,11 @@ export default function Login() {
const [email, setEmail] = useState(''); const [email, setEmail] = useState('');
const [password, setPassword] = useState(''); const [password, setPassword] = useState('');
const [error, setError] = useState(''); const [error, setError] = useState('');
const navigate = useNavigate(); const navigate = useNavigate();
const { login } = useAuth(); const { login } = useAuth();
const [searchParams] = useSearchParams(); const [searchParams] = useSearchParams();
const googleLoginProcessed = useRef(false); const googleLoginProcessed = useRef(false);
useEffect(() => { useEffect(() => {
@@ -22,23 +24,28 @@ export default function Login() {
url.searchParams.delete('token'); url.searchParams.delete('token');
window.history.replaceState({}, '', url); window.history.replaceState({}, '', url);
const fetchUserAndLogin = async () => { try {
try { const base64Url = token.split('.')[1];
localStorage.setItem('token', token); const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
const jsonPayload = decodeURIComponent(window.atob(base64).split('').map(function(c) {
const realUserData = await authService.getCurrentUser(); return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
}).join(''));
login({ user: realUserData, token });
const decodedToken = JSON.parse(jsonPayload);
navigate('/', { replace: true }); const googleUser = {
} catch (err) { _id: decodedToken.sub || decodedToken.user_id || "google_id",
console.error("Error al sincronizar perfil de Google:", err); username: decodedToken.email ? decodedToken.email.split('@')[0] : "Usuario Google",
setError("Error de sincronización con Google. Inténtalo de nuevo."); email: decodedToken.email || ""
localStorage.removeItem('token'); };
}
};
fetchUserAndLogin(); login({ user: googleUser, access_token: token });
navigate('/', { replace: true });
} catch (err) {
console.error("Error al decodificar el token de Google:", err);
setError("Error al procesar el login con Google. El token está corrupto.");
}
} }
}, [searchParams, login, navigate]); }, [searchParams, login, navigate]);
@@ -46,8 +53,8 @@ export default function Login() {
e.preventDefault(); e.preventDefault();
setError(''); setError('');
try { try {
const userData = await authService.login(email, password); const data = await authService.login(email, password);
login(userData); login(data);
navigate('/'); navigate('/');
} catch (err) { } catch (err) {
setError('Credenciales incorrectas.'); setError('Credenciales incorrectas.');
@@ -68,19 +75,31 @@ export default function Login() {
</div> </div>
{error && ( {error && (
<div className="bg-red-50 text-red-600 p-4 rounded-2xl text-sm font-bold mb-6 border border-red-100 text-center">{error}</div> <div className="bg-red-50 text-red-600 p-4 rounded-2xl text-sm font-bold mb-6 border border-red-100 text-center">
{error}
</div>
)} )}
<form onSubmit={handleSubmit} className="space-y-4"> <form onSubmit={handleSubmit} className="space-y-4">
<div className="space-y-1"> <div className="space-y-1">
<label className="text-sm font-bold text-slate-700 ml-1">Email</label> <label className="text-sm font-bold text-slate-700 ml-1">Email</label>
<input type="email" required value={email} onChange={(e) => setEmail(e.target.value)} className="w-full px-5 py-3 rounded-2xl border border-slate-200 focus:ring-2 focus:ring-blue-500 outline-none transition-all bg-slate-50 focus:bg-white" placeholder="correo@ejemplo.com" /> <input
type="email" required value={email} onChange={(e) => setEmail(e.target.value)}
className="w-full px-5 py-3 rounded-2xl border border-slate-200 focus:ring-2 focus:ring-blue-500 outline-none transition-all bg-slate-50 focus:bg-white"
placeholder="correo@ejemplo.com"
/>
</div> </div>
<div className="space-y-1"> <div className="space-y-1">
<label className="text-sm font-bold text-slate-700 ml-1">Contraseña</label> <label className="text-sm font-bold text-slate-700 ml-1">Contraseña</label>
<input type="password" required value={password} onChange={(e) => setPassword(e.target.value)} className="w-full px-5 py-3 rounded-2xl border border-slate-200 focus:ring-2 focus:ring-blue-500 outline-none transition-all bg-slate-50 focus:bg-white" placeholder="••••••••" /> <input
type="password" required value={password} onChange={(e) => setPassword(e.target.value)}
className="w-full px-5 py-3 rounded-2xl border border-slate-200 focus:ring-2 focus:ring-blue-500 outline-none transition-all bg-slate-50 focus:bg-white"
placeholder="••••••••"
/>
</div> </div>
<button type="submit" className="w-full py-4 bg-blue-600 hover:bg-blue-700 text-white font-bold rounded-2xl transition-all shadow-sm active:scale-95 mt-2">Entrar</button> <button type="submit" className="w-full py-4 bg-blue-600 hover:bg-blue-700 text-white font-bold rounded-2xl transition-all shadow-sm active:scale-95 mt-2">
Entrar
</button>
</form> </form>
<div className="relative my-8"> <div className="relative my-8">