e / auth.py
Shaikhsarib's picture
Upload 5 files
a17af42 verified
"""
app/routes/auth.py
Auth endpoints: OTP request, OTP verify, logout, profile.
Wraps app/services/auth.py logic.
"""
import logging
from fastapi import APIRouter, Request, Form, HTTPException
from fastapi.responses import JSONResponse
from app.services.auth import (
send_email_otp, verify_email_otp,
create_session, revoke_session, get_user_from_token,
)
from app.models.db import db_conn
logger = logging.getLogger(__name__)
router = APIRouter(prefix="/auth", tags=["auth"])
@router.post("/request-otp")
async def request_otp(email: str = Form(...)):
"""Send a 6-digit OTP to the user's email. Dev mode: returns OTP in response."""
try:
otp = send_email_otp(email)
return JSONResponse({
"sent" : True,
"message": "OTP sent. Check your email.",
# Remove next line in production — only for dev/HF testing
"_dev_otp": otp,
})
except Exception as exc:
logger.error("OTP request failed: %s", exc)
raise HTTPException(status_code=500, detail="Could not send OTP")
@router.post("/verify-otp")
async def verify_otp(
request: Request,
email: str = Form(...),
otp : str = Form(...),
):
"""Verify OTP. Returns session token on success."""
user = verify_email_otp(email, otp)
if not user:
raise HTTPException(status_code=401, detail="Invalid or expired OTP")
device_hint = request.headers.get("user-agent", "")[:100]
token = create_session(user["id"], device_hint)
return JSONResponse({
"token" : token,
"user_id" : user["id"],
"email" : user.get("email", ""),
"is_pro" : bool(user.get("is_pro", 0)),
"message" : "Login successful",
})
@router.post("/logout")
async def logout(request: Request):
"""Revoke the current session token."""
auth = request.headers.get("Authorization", "")
token = auth.removeprefix("Bearer ").strip() if auth.startswith("Bearer ") else None
if token:
revoke_session(token)
return JSONResponse({"logged_out": True})
@router.get("/me")
async def get_me(request: Request):
"""Return current user profile from Bearer token."""
auth = request.headers.get("Authorization", "")
token = auth.removeprefix("Bearer ").strip() if auth.startswith("Bearer ") else None
user = get_user_from_token(token) if token else None
if not user:
raise HTTPException(status_code=401, detail="Not authenticated")
return JSONResponse({
"user_id" : user["id"],
"email" : user.get("email", ""),
"name" : user.get("name", ""),
"is_pro" : bool(user.get("is_pro", 0)),
"streak_days": user.get("streak_days", 0),
"persona" : user.get("persona", "General Adult"),
"language" : user.get("language", "en"),
})
@router.put("/profile")
async def update_profile(
request : Request,
name : str = Form(""),
persona : str = Form("General Adult"),
language: str = Form("en"),
tdee : float = Form(2000),
):
"""Update user profile fields."""
auth = request.headers.get("Authorization", "")
token = auth.removeprefix("Bearer ").strip() if auth.startswith("Bearer ") else None
user = get_user_from_token(token) if token else None
if not user:
raise HTTPException(status_code=401, detail="Not authenticated")
with db_conn() as conn:
conn.execute(
"UPDATE users SET name=?, persona=?, language=?, tdee=? WHERE id=?",
(name, persona, language, tdee, user["id"])
)
return JSONResponse({"updated": True})