- Backend: users table + admin seed (004), /api/auth endpoints, JWT auth dep gating benchmarks + research routes - Frontend: AuthContext, LoginPage, ProtectedRoute, subpath-aware via VITE_BASE / import.meta.env.BASE_URL so same build works at /opt/ - deploy/: Dockerfile.prod, docker-compose.prod.yml, Apache vhost fragment template, and idempotent deploy.sh (port scan, rsync, env generation, Apache Include + reload) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
43 lines
1.2 KiB
Python
43 lines
1.2 KiB
Python
from fastapi import APIRouter, Depends, HTTPException, status
|
|
from pydantic import BaseModel, EmailStr
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
|
|
from app.database import get_db
|
|
from app.deps import get_current_user
|
|
from app.models import User
|
|
from app.services.auth_service import authenticate, create_access_token
|
|
|
|
router = APIRouter(prefix="/auth", tags=["auth"])
|
|
|
|
|
|
class LoginRequest(BaseModel):
|
|
email: EmailStr
|
|
password: str
|
|
|
|
|
|
class LoginResponse(BaseModel):
|
|
access_token: str
|
|
token_type: str = "bearer"
|
|
email: str
|
|
|
|
|
|
class MeResponse(BaseModel):
|
|
id: int
|
|
email: str
|
|
|
|
|
|
@router.post("/login", response_model=LoginResponse)
|
|
async def login(body: LoginRequest, db: AsyncSession = Depends(get_db)):
|
|
user = await authenticate(db, body.email, body.password)
|
|
if not user:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_401_UNAUTHORIZED,
|
|
detail="Invalid email or password",
|
|
)
|
|
token = create_access_token(user.id)
|
|
return LoginResponse(access_token=token, email=user.email)
|
|
|
|
|
|
@router.get("/me", response_model=MeResponse)
|
|
async def me(user: User = Depends(get_current_user)):
|
|
return MeResponse(id=user.id, email=user.email)
|