import uuid import datetime import hashlib import os from fastapi import APIRouter, Depends, HTTPException from sqlalchemy.orm import Session from jose import jwt from app.database import get_db from app.models.user import User from app.models.apikey import APIKey from app.dependencies import hash_api_key, get_current_user from app.schemas import UserRegister, UserLogin, TokenResponse, APIKeyResponse from app.config import get_settings router = APIRouter(prefix="/v1/auth", tags=["auth"]) settings = get_settings() def hash_password(password: str) -> str: salt = os.urandom(16).hex() h = hashlib.sha256((password + salt).encode()).hexdigest() return f"{salt}${h}" def verify_password(password: str, hashed: str) -> bool: salt, h = hashed.split("$", 1) return hashlib.sha256((password + salt).encode()).hexdigest() == h def generate_api_key() -> tuple: raw = f"revai_live_{uuid.uuid4().hex}{uuid.uuid4().hex[:8]}" return raw, hash_api_key(raw) def create_token(user: User) -> str: payload = { "sub": user.id, "email": user.email, "tier": user.tier, "exp": datetime.datetime.utcnow() + datetime.timedelta(minutes=settings.access_token_expire_minutes), } return jwt.encode(payload, settings.secret_key, algorithm=settings.algorithm) @router.post("/register", response_model=TokenResponse) def register(body: UserRegister, db: Session = Depends(get_db)): existing = db.query(User).filter(User.email == body.email).first() if existing: raise HTTPException(status_code=409, detail="Email already registered") user = User( email=body.email, hashed_password=hash_password(body.password), name=body.name, ) db.add(user) db.flush() raw_key, key_hash = generate_api_key() api_key = APIKey( user_id=user.id, key_hash=key_hash, prefix=raw_key[:20] + "...", ) db.add(api_key) db.commit() token = create_token(user) return TokenResponse( access_token=token, user_id=user.id, email=user.email, tier=user.tier, api_key=raw_key, ) @router.post("/login", response_model=TokenResponse) def login(body: UserLogin, db: Session = Depends(get_db)): user = db.query(User).filter(User.email == body.email).first() if not user or not verify_password(body.password, user.hashed_password): raise HTTPException(status_code=401, detail="Invalid email or password") # Return existing active key or create new one api_key = db.query(APIKey).filter( APIKey.user_id == user.id, APIKey.is_active == True ).first() raw_key = None if not api_key: raw_key, key_hash = generate_api_key() api_key = APIKey(user_id=user.id, key_hash=key_hash, prefix=raw_key[:20] + "...") db.add(api_key) db.commit() raw_key = raw_key token = create_token(user) return TokenResponse( access_token=token, user_id=user.id, email=user.email, tier=user.tier, api_key=raw_key, ) @router.get("/keys", response_model=list[APIKeyResponse]) def list_api_keys(user: User = Depends(get_current_user), db: Session = Depends(get_db)): keys = db.query(APIKey).filter(APIKey.user_id == user.id).all() return [APIKeyResponse( id=k.id, prefix=k.prefix, name=k.name, is_active=k.is_active, created_at=k.created_at, last_used_at=k.last_used_at ) for k in keys]