nanochat/services/auth/src/routes/users.py
Manmohan Sharma 4b4aca642a
feat(auth): OAuth2 + JWT auth service with Alembic migrations (#5 #7)
- Alembic async migrations: users, conversations, messages, is_favorited
- FastAPI auth service: Google + GitHub OAuth, RS256 JWT, refresh cookie
- /auth/me, /auth/refresh, /auth/validate (service-to-service)
- rate limiting 10/min on OAuth routes, CORS locked to FRONTEND_URL

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-16 11:47:00 -07:00

47 lines
1.3 KiB
Python

"""User profile routes (GET /auth/me, PUT /auth/me)."""
from __future__ import annotations
from fastapi import APIRouter, Depends
from pydantic import BaseModel, Field
from sqlalchemy.ext.asyncio import AsyncSession
from ..database import get_session
from ..middleware.auth_middleware import AuthContext, require_user
from ..services import user_service
router = APIRouter(prefix="/auth", tags=["users"])
class UserProfile(BaseModel):
id: str
email: str
name: str | None
avatar_url: str | None
provider: str
provider_id: str
created_at: str | None
updated_at: str | None
last_login_at: str | None
class ProfileUpdate(BaseModel):
name: str | None = Field(default=None, max_length=255)
avatar_url: str | None = Field(default=None, max_length=2048)
@router.get("/me", response_model=UserProfile)
async def me(ctx: AuthContext = Depends(require_user)) -> UserProfile:
return UserProfile(**ctx.user.to_dict())
@router.put("/me", response_model=UserProfile)
async def update_me(
payload: ProfileUpdate,
ctx: AuthContext = Depends(require_user),
session: AsyncSession = Depends(get_session),
) -> UserProfile:
user = await user_service.update_profile(
session, ctx.user, name=payload.name, avatar_url=payload.avatar_url
)
return UserProfile(**user.to_dict())