from datetime import datetime, timedelta from fastapi import APIRouter, HTTPException, Depends, status from pydantic import BaseModel, EmailStr import jwt from passlib.context import CryptContext from app.core.config import settings from app.db.database import get_connection router = APIRouter() pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto") class UserRegister(BaseModel): email: EmailStr phone: str | None = None password: str class UserLogin(BaseModel): email: EmailStr password: str class TokenResponse(BaseModel): access_token: str token_type: str = "bearer" def hash_password(password: str) -> str: return pwd_context.hash(password) def verify_password(plain: str, hashed: str) -> bool: return pwd_context.verify(plain, hashed) def create_token(user_id: int) -> str: expire = datetime.utcnow() + timedelta(minutes=settings.access_token_expire_minutes) payload = {"sub": str(user_id), "exp": expire} return jwt.encode(payload, settings.secret_key, algorithm=settings.algorithm) @router.post("/register", response_model=TokenResponse) async def register(user: UserRegister): conn = get_connection() existing = conn.execute( "SELECT id FROM users WHERE email = ?", (user.email,) ).fetchone() if existing: raise HTTPException(status_code=400, detail="Email already registered") password_hash = hash_password(user.password) cursor = conn.execute( "INSERT INTO users (email, phone, password_hash) VALUES (?, ?, ?) RETURNING id", (user.email, user.phone, password_hash) ) user_id = cursor.fetchone()[0] # Create default preferences conn.execute( "INSERT INTO notification_preferences (user_id) VALUES (?)", (user_id,) ) conn.commit() conn.close() token = create_token(user_id) return TokenResponse(access_token=token) @router.post("/login", response_model=TokenResponse) async def login(user: UserLogin): conn = get_connection() db_user = conn.execute( "SELECT id, password_hash FROM users WHERE email = ?", (user.email,) ).fetchone() conn.close() if not db_user or not verify_password(user.password, db_user[1]): raise HTTPException(status_code=401, detail="Invalid credentials") token = create_token(db_user[0]) return TokenResponse(access_token=token)