- 백엔드 API 완전 구현 (FastAPI + SQLAlchemy + PostgreSQL) - 사용자 인증 (JWT 토큰 기반) - 문서 CRUD (업로드, 조회, 목록, 삭제) - 하이라이트, 메모, 책갈피 관리 - 태그 시스템 및 검색 기능 - Pydantic v2 호환성 수정 - 프론트엔드 완전 구현 (Alpine.js + Tailwind CSS) - 로그인/로그아웃 기능 - 문서 업로드 모달 (드래그앤드롭, 파일 검증) - 문서 목록 및 필터링 - 뷰어 페이지 (하이라이트, 메모, 책갈피 UI) - 실시간 목록 새로고침 - 시스템 안정성 개선 - Alpine.js 컴포넌트 간 안전한 통신 (이벤트 기반) - API 오류 처리 및 사용자 피드백 - 파비콘 추가로 404 오류 해결 - 포트 구성: Frontend(24100), Backend(24102), DB(24101), Redis(24103)
89 lines
2.5 KiB
Python
89 lines
2.5 KiB
Python
"""
|
|
API 의존성
|
|
"""
|
|
from fastapi import Depends, HTTPException, status
|
|
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
from sqlalchemy import select
|
|
from typing import Optional
|
|
|
|
from ..core.database import get_db
|
|
from ..core.security import verify_token, get_user_id_from_token
|
|
from ..models.user import User
|
|
|
|
|
|
# HTTP Bearer 토큰 스키마
|
|
security = HTTPBearer()
|
|
|
|
|
|
async def get_current_user(
|
|
credentials: HTTPAuthorizationCredentials = Depends(security),
|
|
db: AsyncSession = Depends(get_db)
|
|
) -> User:
|
|
"""현재 로그인된 사용자 가져오기"""
|
|
try:
|
|
# 토큰에서 사용자 ID 추출
|
|
user_id = get_user_id_from_token(credentials.credentials)
|
|
|
|
# 데이터베이스에서 사용자 조회
|
|
result = await db.execute(select(User).where(User.id == user_id))
|
|
user = result.scalar_one_or_none()
|
|
|
|
if not user:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_401_UNAUTHORIZED,
|
|
detail="User not found"
|
|
)
|
|
|
|
if not user.is_active:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_401_UNAUTHORIZED,
|
|
detail="Inactive user"
|
|
)
|
|
|
|
return user
|
|
|
|
except Exception as e:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_401_UNAUTHORIZED,
|
|
detail="Could not validate credentials"
|
|
)
|
|
|
|
|
|
async def get_current_active_user(
|
|
current_user: User = Depends(get_current_user)
|
|
) -> User:
|
|
"""활성 사용자 확인"""
|
|
if not current_user.is_active:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_400_BAD_REQUEST,
|
|
detail="Inactive user"
|
|
)
|
|
return current_user
|
|
|
|
|
|
async def get_current_admin_user(
|
|
current_user: User = Depends(get_current_active_user)
|
|
) -> User:
|
|
"""관리자 권한 확인"""
|
|
if not current_user.is_admin:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_403_FORBIDDEN,
|
|
detail="Not enough permissions"
|
|
)
|
|
return current_user
|
|
|
|
|
|
async def get_optional_current_user(
|
|
credentials: Optional[HTTPAuthorizationCredentials] = Depends(security),
|
|
db: AsyncSession = Depends(get_db)
|
|
) -> Optional[User]:
|
|
"""선택적 사용자 인증 (토큰이 없어도 됨)"""
|
|
if not credentials:
|
|
return None
|
|
|
|
try:
|
|
return await get_current_user(credentials, db)
|
|
except HTTPException:
|
|
return None
|