Multi-tenant factory inspection system (SpiFox, Enkid, Alpet): - FastAPI backend with JWT auth, PostgreSQL (asyncpg) - Next.js 16 frontend with App Router, SWR data fetching - Machines CRUD with equipment parts management - Part lifecycle tracking (hours/count/date) with counters - Partial unique index for soft-delete support - 24 pytest tests passing, E2E verified Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>
103 lines
3.3 KiB
Python
103 lines
3.3 KiB
Python
from typing import List
|
|
|
|
from fastapi import APIRouter, HTTPException, Response, Depends
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
|
|
from src.database.config import get_db
|
|
from src.auth.models import UserLogin, UserCreate, UserResponse, Token, TokenData
|
|
from src.auth import service as auth_service
|
|
from src.auth.dependencies import require_auth, require_superadmin
|
|
|
|
router = APIRouter(prefix="/api/auth", tags=["auth"])
|
|
admin_router = APIRouter(prefix="/api/admin", tags=["admin"])
|
|
|
|
|
|
@router.post("/login", response_model=Token)
|
|
async def login(
|
|
credentials: UserLogin,
|
|
response: Response,
|
|
db: AsyncSession = Depends(get_db),
|
|
):
|
|
"""로그인 - 이메일/비밀번호로 JWT 토큰 발급"""
|
|
result = await auth_service.login(db, credentials.email, credentials.password)
|
|
if not result:
|
|
raise HTTPException(
|
|
status_code=401, detail="이메일 또는 비밀번호가 올바르지 않습니다."
|
|
)
|
|
|
|
response.set_cookie(
|
|
key="access_token",
|
|
value=result.access_token,
|
|
httponly=True,
|
|
max_age=86400,
|
|
samesite="lax",
|
|
)
|
|
return result
|
|
|
|
|
|
@router.post("/logout")
|
|
async def logout(response: Response):
|
|
"""로그아웃"""
|
|
response.delete_cookie(key="access_token")
|
|
return {"status": "success", "message": "로그아웃되었습니다."}
|
|
|
|
|
|
@router.get("/me", response_model=UserResponse)
|
|
async def get_current_user_info(
|
|
current_user: TokenData = Depends(require_auth),
|
|
db: AsyncSession = Depends(get_db),
|
|
):
|
|
"""현재 로그인한 사용자 정보"""
|
|
user = await auth_service.get_user_by_id(db, current_user.user_id)
|
|
if not user:
|
|
raise HTTPException(status_code=404, detail="사용자를 찾을 수 없습니다.")
|
|
return UserResponse(
|
|
id=str(user.id),
|
|
email=str(user.email),
|
|
name=str(user.name),
|
|
role=str(user.role),
|
|
tenant_id=str(user.tenant_id) if user.tenant_id is not None else None,
|
|
is_active=bool(user.is_active),
|
|
)
|
|
|
|
|
|
@admin_router.get("/users", response_model=List[UserResponse])
|
|
async def list_all_users(
|
|
current_user: TokenData = Depends(require_superadmin),
|
|
db: AsyncSession = Depends(get_db),
|
|
):
|
|
"""전체 사용자 목록 (superadmin 전용)"""
|
|
users = await auth_service.list_users(db)
|
|
return [
|
|
UserResponse(
|
|
id=str(u.id),
|
|
email=str(u.email),
|
|
name=str(u.name),
|
|
role=str(u.role),
|
|
tenant_id=str(u.tenant_id) if u.tenant_id is not None else None,
|
|
is_active=bool(u.is_active),
|
|
)
|
|
for u in users
|
|
]
|
|
|
|
|
|
@admin_router.post("/users", response_model=UserResponse)
|
|
async def create_user(
|
|
user_data: UserCreate,
|
|
current_user: TokenData = Depends(require_superadmin),
|
|
db: AsyncSession = Depends(get_db),
|
|
):
|
|
"""사용자 생성 (superadmin 전용)"""
|
|
try:
|
|
user = await auth_service.create_user(db, user_data)
|
|
return UserResponse(
|
|
id=str(user.id),
|
|
email=str(user.email),
|
|
name=str(user.name),
|
|
role=str(user.role),
|
|
tenant_id=str(user.tenant_id) if user.tenant_id is not None else None,
|
|
is_active=bool(user.is_active),
|
|
)
|
|
except ValueError as e:
|
|
raise HTTPException(status_code=400, detail=str(e))
|