studio / auth /routes.py
Ava2lon's picture
Upload 170 files
345855e verified
Raw
History Blame Contribute Delete
7.95 kB
from fastapi import APIRouter, Depends, HTTPException, status
from sqlalchemy.orm import Session
from sqlalchemy.exc import IntegrityError
from datetime import datetime
from auth.database import get_db
from auth.models import User
from auth.schemas import (
SignupSchema,
LoginSchema,
TokenSchema,
)
from auth.security import (
hash_password,
verify_password,
create_access_token,
decode_token,
)
router = APIRouter(
prefix="/api/auth",
tags=["Authentication"],
)
# =========================================================
# SIGNUP
# =========================================================
@router.post("/signup", status_code=201)
def signup(
data: SignupSchema,
db: Session = Depends(get_db),
):
try:
# =================================================
# NORMALIZATION
# =================================================
email = data.email.strip().lower()
username = data.username.strip()
# =================================================
# EXISTING EMAIL CHECK
# =================================================
existing_email = (
db.query(User)
.filter(User.email == email)
.first()
)
if existing_email:
raise HTTPException(
status_code=status.HTTP_409_CONFLICT,
detail="Email already registered",
)
# =================================================
# EXISTING USERNAME CHECK
# =================================================
existing_username = (
db.query(User)
.filter(User.username == username)
.first()
)
if existing_username:
raise HTTPException(
status_code=status.HTTP_409_CONFLICT,
detail="Username already taken",
)
# =================================================
# HASH PASSWORD
# =================================================
password_hash = hash_password(data.password)
# =================================================
# CREATE USER
# =================================================
user = User(
email=email,
username=username,
password_hash=password_hash,
created_at=datetime.utcnow(),
)
db.add(user)
db.commit()
db.refresh(user)
# =================================================
# CREATE JWT
# =================================================
token = create_access_token(
{
"sub": str(user.id),
"email": user.email,
}
)
return {
"status": "success",
"message": "Account created successfully",
"token": token,
"user": {
"id": str(user.id),
"email": user.email,
"username": user.username,
"created_at": (
user.created_at.isoformat()
if user.created_at
else None
),
},
}
except HTTPException:
raise
except IntegrityError as e:
db.rollback()
raise HTTPException(
status_code=status.HTTP_409_CONFLICT,
detail="User already exists",
)
except ValueError as e:
db.rollback()
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=str(e),
)
except Exception as e:
db.rollback()
print("SIGNUP ERROR:", str(e))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"Signup failed: {str(e)}",
)
# =========================================================
# LOGIN
# =========================================================
@router.post("/login")
def login(
data: LoginSchema,
db: Session = Depends(get_db),
):
try:
email = data.email.strip().lower()
user = (
db.query(User)
.filter(User.email == email)
.first()
)
if not user:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Invalid email or password",
)
if not verify_password(
data.password,
user.password_hash,
):
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Invalid email or password",
)
token = create_access_token(
{
"sub": str(user.id),
"email": user.email,
}
)
return {
"status": "success",
"message": "Login successful",
"token": token,
"user": {
"id": str(user.id),
"email": user.email,
"username": user.username,
"created_at": (
user.created_at.isoformat()
if user.created_at
else None
),
},
}
except HTTPException:
raise
except Exception as e:
print("LOGIN ERROR:", str(e))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"Login failed: {str(e)}",
)
# =========================================================
# VERIFY TOKEN
# =========================================================
@router.post("/verify")
def verify_token(
data: TokenSchema,
):
try:
payload = decode_token(data.token)
if not payload:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Invalid token",
)
return {
"valid": True,
"payload": payload,
}
except HTTPException:
raise
except Exception as e:
print("VERIFY ERROR:", str(e))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"Token verification failed: {str(e)}",
)
# =========================================================
# REFRESH TOKEN
# =========================================================
@router.post("/refresh")
def refresh_token(
data: TokenSchema,
db: Session = Depends(get_db),
):
try:
payload = decode_token(data.token)
user_id = payload.get("sub")
if not user_id:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Invalid token payload",
)
user = (
db.query(User)
.filter(User.id == user_id)
.first()
)
if not user:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="User not found",
)
new_token = create_access_token(
{
"sub": str(user.id),
"email": user.email,
}
)
return {
"status": "success",
"token": new_token,
}
except HTTPException:
raise
except Exception as e:
print("REFRESH ERROR:", str(e))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"Refresh failed: {str(e)}",
)
# =========================================================
# LOGOUT
# =========================================================
@router.post("/logout")
def logout():
return {
"status": "success",
"message": "Logout successful",
}