You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
77 lines
2.8 KiB
Python
77 lines
2.8 KiB
Python
|
1 day ago
|
from fastapi import APIRouter, Depends, HTTPException, Request
|
||
|
|
from sqlmodel import Session, select
|
||
|
|
from datetime import datetime
|
||
|
|
|
||
|
|
from app.core.db import get_session
|
||
|
|
from app.models import GoogleAccount
|
||
|
|
from app.google.auth import build_flow, token_json_from_creds
|
||
|
|
from app.google.client import get_service
|
||
|
|
|
||
|
|
router = APIRouter()
|
||
|
|
|
||
|
|
@router.get("/start")
|
||
|
|
def start_oauth():
|
||
|
|
"""Devuelve el auth_url para que el usuario lo abra y autorice."""
|
||
|
|
flow = build_flow()
|
||
|
|
auth_url, state = flow.authorization_url(
|
||
|
|
access_type="offline",
|
||
|
|
include_granted_scopes="true",
|
||
|
|
prompt="consent",
|
||
|
|
)
|
||
|
|
return {"auth_url": auth_url, "state": state}
|
||
|
|
|
||
|
|
@router.get("/callback")
|
||
|
|
def oauth_callback(request: Request, session: Session = Depends(get_session)):
|
||
|
|
"""Recibe code/state y guarda token en SQLite."""
|
||
|
|
code = request.query_params.get("code")
|
||
|
|
state = request.query_params.get("state")
|
||
|
|
if not code:
|
||
|
|
raise HTTPException(status_code=400, detail="Falta 'code' en el callback")
|
||
|
|
|
||
|
|
flow = build_flow(state=state)
|
||
|
|
try:
|
||
|
|
flow.fetch_token(code=code)
|
||
|
|
except Exception as e:
|
||
|
|
raise HTTPException(status_code=400, detail=f"No se pudo obtener token: {e}")
|
||
|
|
|
||
|
|
creds = flow.credentials
|
||
|
|
token_json = token_json_from_creds(creds)
|
||
|
|
|
||
|
|
# Obtenemos el email con OAuth2 userinfo
|
||
|
|
try:
|
||
|
|
oauth2 = get_service("oauth2", "v2", token_json)
|
||
|
|
info = oauth2.userinfo().get().execute()
|
||
|
|
email = info.get("email")
|
||
|
|
if not email:
|
||
|
|
raise ValueError("No se pudo leer email")
|
||
|
|
except Exception as e:
|
||
|
|
raise HTTPException(status_code=400, detail=f"Token ok pero no pude obtener email: {e}")
|
||
|
|
|
||
|
|
existing = session.exec(select(GoogleAccount).where(GoogleAccount.email == email)).first()
|
||
|
|
now = datetime.utcnow()
|
||
|
|
if existing:
|
||
|
|
existing.token_json = token_json
|
||
|
|
existing.updated_at = now
|
||
|
|
session.add(existing)
|
||
|
|
session.commit()
|
||
|
|
return {"status": "updated", "email": email}
|
||
|
|
else:
|
||
|
|
acc = GoogleAccount(email=email, token_json=token_json, created_at=now, updated_at=now)
|
||
|
|
session.add(acc)
|
||
|
|
session.commit()
|
||
|
|
return {"status": "created", "email": email}
|
||
|
|
|
||
|
|
@router.get("/accounts")
|
||
|
|
def list_accounts(session: Session = Depends(get_session)):
|
||
|
|
rows = session.exec(select(GoogleAccount)).all()
|
||
|
|
return [{"email": r.email, "created_at": r.created_at, "updated_at": r.updated_at} for r in rows]
|
||
|
|
|
||
|
|
@router.delete("/accounts/{email}")
|
||
|
|
def delete_account(email: str, session: Session = Depends(get_session)):
|
||
|
|
acc = session.exec(select(GoogleAccount).where(GoogleAccount.email == email)).first()
|
||
|
|
if not acc:
|
||
|
|
raise HTTPException(status_code=404, detail="Cuenta no encontrada")
|
||
|
|
session.delete(acc)
|
||
|
|
session.commit()
|
||
|
|
return {"status": "deleted", "email": email}
|