from __future__ import annotations import hashlib from typing import Optional import aiosqlite from fastapi import APIRouter, Depends, Request from api.deps import error_response, get_db from common import ApiModel from users import repository as users_db from users.models import User router = APIRouter(prefix="/auth", tags=["auth-v2"]) class RegisterBody(ApiModel): email: str password: str display_name: str class LoginBody(ApiModel): email: str password: str class TokenResponse(ApiModel): access_token: str token_type: str = "bearer" user: User def _hash_pw(pw: str) -> str: # Placeholder; replace with proper hashing (bcrypt/argon2) later return hashlib.sha256(pw.encode("utf-8")).hexdigest() @router.post("/register", response_model=TokenResponse, operation_id="register") async def register(request: Request, body: RegisterBody, conn: aiosqlite.Connection = Depends(get_db)): existing = await users_db.get_by_email(conn, body.email) if existing: return error_response(request, 400, "Email already registered") uid = await users_db.insert_user(conn, body.email, body.display_name) await users_db.set_local_credentials(conn, uid, _hash_pw(body.password)) user = await users_db.get_by_email(conn, body.email) assert user is not None # Token is a simple placeholder containing user id; will be replaced with JWT token = f"user-{user.id}" return TokenResponse(access_token=token, user=user) @router.post("/login", response_model=TokenResponse, operation_id="loginV2") async def login(request: Request, body: LoginBody, conn: aiosqlite.Connection = Depends(get_db)): user: Optional[User] = await users_db.get_by_email(conn, body.email) if not user: return error_response(request, 401, "Invalid credentials") stored = await users_db.get_local_password_hash(conn, user.id) if not stored or stored != _hash_pw(body.password): return error_response(request, 401, "Invalid credentials") token = f"user-{user.id}" return TokenResponse(access_token=token, user=user)