Spaces:
Sleeping
Sleeping
| from fastapi import APIRouter, HTTPException, Response, Request | |
| from pydantic import BaseModel, EmailStr | |
| from config.database import get_supabase, get_supabase_admin | |
| from config.settings import get_settings | |
| router = APIRouter(prefix="/api/auth", tags=["auth"]) | |
| settings = get_settings() | |
| class RegisterRequest(BaseModel): | |
| email: EmailStr | |
| password: str | |
| display_name: str = "" | |
| class LoginRequest(BaseModel): | |
| email: EmailStr | |
| password: str | |
| async def register(data: RegisterRequest): | |
| supabase = get_supabase() | |
| try: | |
| response = supabase.auth.sign_up({ | |
| "email": data.email, | |
| "password": data.password, | |
| "options": { | |
| "data": { | |
| "full_name": data.display_name or data.email.split("@")[0] | |
| } | |
| } | |
| }) | |
| if response.user is None: | |
| raise HTTPException(status_code=400, detail="Registration failed") | |
| # Sync to public users table | |
| admin_db = get_supabase_admin() | |
| admin_db.table("users").insert({ | |
| "id": response.user.id, | |
| "email": data.email, | |
| "display_name": data.display_name or data.email.split("@")[0], | |
| "role": "free" | |
| }).execute() | |
| return {"message": "Registration successful. Please check your email to verify your account."} | |
| except Exception as e: | |
| error_msg = str(e) | |
| if "23505" in error_msg or "users_pkey" in error_msg: | |
| raise HTTPException(status_code=400, detail="An account with this email already exists.") | |
| # Hide raw database dictionary strings if they slip through | |
| if error_msg.startswith("{") and "'code'" in error_msg: | |
| import ast | |
| try: | |
| err_dict = ast.literal_eval(error_msg) | |
| if isinstance(err_dict, dict) and "message" in err_dict: | |
| # Still, we might not want to expose raw DB messages, but it's better than the dict | |
| raise HTTPException(status_code=400, detail=err_dict["message"]) | |
| except (ValueError, SyntaxError): | |
| pass | |
| raise HTTPException(status_code=400, detail="An error occurred during registration.") | |
| raise HTTPException(status_code=400, detail=error_msg) | |
| async def login(data: LoginRequest, response: Response): | |
| supabase = get_supabase() | |
| try: | |
| auth_response = supabase.auth.sign_in_with_password({ | |
| "email": data.email, | |
| "password": data.password | |
| }) | |
| if auth_response.user is None: | |
| raise HTTPException(status_code=401, detail="Invalid email or password") | |
| response.set_cookie( | |
| key="access_token", | |
| value=auth_response.session.access_token, | |
| httponly=True, | |
| secure=True, | |
| samesite="lax", | |
| max_age=604800 | |
| ) | |
| return { | |
| "message": "Login successful", | |
| "user": { | |
| "id": str(auth_response.user.id), | |
| "email": auth_response.user.email, | |
| "display_name": auth_response.user.user_metadata.get("full_name", "") | |
| } | |
| } | |
| except Exception as e: | |
| raise HTTPException(status_code=401, detail="Invalid email or password") | |
| async def logout(response: Response): | |
| response.delete_cookie("access_token") | |
| return {"message": "Logged out successfully"} | |
| async def forgot_password(email: EmailStr): | |
| supabase = get_supabase() | |
| try: | |
| supabase.auth.reset_password_email( | |
| email, | |
| options={"redirect_to": f"{settings.frontend_url}/auth/reset-password"} | |
| ) | |
| return {"message": "Password reset email sent"} | |
| except Exception as e: | |
| raise HTTPException(status_code=400, detail=str(e)) |