aiteamutils 0.2.51__py3-none-any.whl → 0.2.53__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
aiteamutils/database.py CHANGED
@@ -764,11 +764,12 @@ class DatabaseService:
764
764
  processed_data = self.preprocess_data(model, log_data)
765
765
  entity = model(**processed_data)
766
766
 
767
- # 로그 엔티티 저장
768
- self.db.add(entity)
769
- await self.db.flush()
770
-
771
- return entity
767
+ async with self.get_session() as session:
768
+ # 로그 엔티티 저장
769
+ session.add(entity)
770
+ await session.flush()
771
+ await session.commit()
772
+ return entity
772
773
 
773
774
  except Exception as e:
774
775
  logging.error(f"Failed to create log: {str(e)}")
@@ -996,6 +997,24 @@ class DatabaseService:
996
997
  original_error=e
997
998
  )
998
999
 
1000
+ async def create_session(self) -> AsyncSession:
1001
+ """새로운 데이터베이스 세션을 생성합니다.
1002
+
1003
+ Returns:
1004
+ AsyncSession: 생성된 세션
1005
+
1006
+ Raises:
1007
+ CustomException: 세션 생성 실패 시
1008
+ """
1009
+ if self.session_factory is None:
1010
+ raise CustomException(
1011
+ ErrorCode.DB_CONNECTION_ERROR,
1012
+ detail="session_factory is not initialized",
1013
+ source_function="DatabaseService.create_session"
1014
+ )
1015
+
1016
+ return self.session_factory()
1017
+
999
1018
  async def get_db() -> AsyncGenerator[AsyncSession, None]:
1000
1019
  """데이터베이스 세션 의존성
1001
1020
 
@@ -1,135 +1,213 @@
1
- from typing import Type, Dict, Tuple, Any, Callable
2
- from fastapi import Depends, status
3
- from fastapi.security import OAuth2PasswordBearer
4
- from jose import JWTError, jwt
1
+ """의존성 관리 모듈."""
2
+ from typing import Type, TypeVar, Dict, Any, Optional, Callable, List, AsyncGenerator
3
+ from fastapi import Request, Depends, HTTPException, status
5
4
  from sqlalchemy.ext.asyncio import AsyncSession
5
+ from jose import jwt, JWTError
6
6
  import logging
7
7
 
8
- from .database import DatabaseServiceManager, DatabaseService, get_db, get_database_service
9
8
  from .exceptions import CustomException, ErrorCode
10
9
  from .config import get_settings
10
+ from .base_service import BaseService
11
+ from .base_repository import BaseRepository
12
+ from .database import db_manager
11
13
 
12
- class ServiceRegistry:
13
- """서비스 레지스트리 클래스"""
14
- def __init__(self):
15
- self._services: Dict[str, Tuple[Type[Any], Type[Any]]] = {}
14
+ T = TypeVar("T", bound=BaseService)
15
+ R = TypeVar("R", bound=BaseRepository)
16
+
17
+ _service_registry: Dict[str, Dict[str, Any]] = {}
18
+
19
+ async def get_db() -> AsyncGenerator[AsyncSession, None]:
20
+ """데이터베이스 세션을 반환합니다.
16
21
 
17
- def register(self, name: str, repository_class: Type[Any], service_class: Type[Any]) -> None:
18
- """서비스를 등록합니다.
22
+ Yields:
23
+ AsyncSession: 데이터베이스 세션
19
24
 
20
- Args:
21
- name (str): 서비스 이름
22
- repository_class (Type[Any]): 레포지토리 클래스
23
- service_class (Type[Any]): 서비스 클래스
24
- """
25
- self._services[name] = (repository_class, service_class)
25
+ Raises:
26
+ CustomException: 세션 생성 실패 시
27
+ """
28
+ try:
29
+ async with db_manager.get_session() as session:
30
+ yield session
31
+ except Exception as e:
32
+ raise CustomException(
33
+ ErrorCode.DATABASE_ERROR,
34
+ detail=str(e),
35
+ source_function="dependencies.get_db",
36
+ original_error=e
37
+ )
38
+
39
+ def register_service(
40
+ service_class: Type[T],
41
+ repository_class: Optional[Type[R]] = None,
42
+ **kwargs
43
+ ) -> None:
44
+ """서비스를 등록합니다.
26
45
 
27
- def get(self, name: str) -> Tuple[Type[Any], Type[Any]]:
28
- """등록된 서비스를 가져옵니다.
46
+ Args:
47
+ service_class: 서비스 클래스
48
+ repository_class: 저장소 클래스 (선택)
49
+ **kwargs: 추가 의존성
50
+ """
51
+ service_name = service_class.__name__
52
+ _service_registry[service_name] = {
53
+ "service_class": service_class,
54
+ "repository_class": repository_class,
55
+ "dependencies": kwargs
56
+ }
57
+
58
+ async def _get_service(
59
+ service_name: str,
60
+ session: AsyncSession,
61
+ request: Request
62
+ ) -> BaseService:
63
+ """서비스 인스턴스를 생성합니다.
64
+
65
+ Args:
66
+ service_name: 서비스 이름
67
+ session: 데이터베이스 세션
68
+ request: FastAPI 요청 객체
29
69
 
30
- Args:
31
- name (str): 서비스 이름
32
-
33
- Returns:
34
- Tuple[Type[Any], Type[Any]]: (레포지토리 클래스, 서비스 클래스)
35
-
36
- Raises:
37
- CustomException: 등록되지 않은 서비스인 경우
38
- """
39
- if name not in self._services:
70
+ Returns:
71
+ BaseService: 서비스 인스턴스
72
+
73
+ Raises:
74
+ CustomException: 서비스 생성 실패
75
+ """
76
+ try:
77
+ service_info = _service_registry.get(service_name)
78
+ if not service_info:
40
79
  raise CustomException(
41
- ErrorCode.NOT_FOUND,
42
- detail=f"Service '{name}' is not registered",
43
- source_function="ServiceRegistry.get"
80
+ ErrorCode.SERVICE_NOT_FOUND,
81
+ detail=service_name,
82
+ source_function="dependencies._get_service"
44
83
  )
45
- return self._services[name]
46
-
47
- # ServiceRegistry 초기화
48
- service_registry = ServiceRegistry()
49
-
50
- def get_service(name: str):
51
- """등록된 서비스를 가져오는 의존성 함수
84
+
85
+ service_class = service_info["service_class"]
86
+ repository_class = service_info["repository_class"]
87
+ dependencies = service_info["dependencies"]
88
+
89
+ # 저장소 인스턴스 생성
90
+ repository = None
91
+ if repository_class:
92
+ repository = repository_class(session=session)
93
+
94
+ # 서비스 인스턴스 생성
95
+ service = service_class(
96
+ repository=repository,
97
+ session=session,
98
+ request=request,
99
+ **dependencies
100
+ )
101
+
102
+ return service
103
+
104
+ except CustomException as e:
105
+ raise e
106
+ except Exception as e:
107
+ raise CustomException(
108
+ ErrorCode.INTERNAL_ERROR,
109
+ detail=str(e),
110
+ source_function="dependencies._get_service",
111
+ original_error=e
112
+ )
52
113
 
114
+ def get_service(service_name: str) -> Callable:
115
+ """서비스 의존성을 반환합니다.
116
+
53
117
  Args:
54
- name (str): 서비스 이름
55
-
118
+ service_name: 서비스 이름
119
+
56
120
  Returns:
57
- Callable: 서비스 인스턴스를 반환하는 의존성 함수
121
+ Callable: 서비스 의존성 함수
58
122
  """
59
- def _get_service(
60
- db_service: DatabaseService = Depends(get_database_service),
123
+ async def _get_service_dependency(
124
+ request: Request,
61
125
  session: AsyncSession = Depends(get_db)
62
- ):
63
- try:
64
- repository_class, service_class = service_registry.get(name)
65
- repository = repository_class(db_service)
66
- service = service_class(repository, db_service)
67
- service.session = session
68
- return service
69
- except CustomException as e:
70
- raise e
71
- except Exception as e:
72
- raise CustomException(
73
- ErrorCode.INTERNAL_ERROR,
74
- detail=f"Failed to create service '{name}': {str(e)}",
75
- source_function="dependencies.get_service",
76
- original_error=e
77
- )
78
- return _get_service
126
+ ) -> BaseService:
127
+ return await _get_service(service_name, session, request)
128
+ return _get_service_dependency
79
129
 
80
- oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/users/token")
81
130
  async def get_current_user(
82
- token: str = Depends(oauth2_scheme),
83
- db_service: DatabaseServiceManager = Depends(get_database_service)
84
- ):
85
- """현재 사용자를 가져오는 의존성 함수
86
-
131
+ request: Request,
132
+ session: AsyncSession = Depends(get_db),
133
+ auth_service: BaseService = Depends(get_service("AuthService"))
134
+ ) -> Dict[str, Any]:
135
+ """현재 사용자 정보를 반환합니다.
136
+
87
137
  Args:
88
- token (str): OAuth2 토큰
89
- db_service (DatabaseServiceManager): DatabaseServiceManager 객체
90
-
138
+ request: FastAPI 요청 객체
139
+ session: 데이터베이스 세션
140
+ auth_service: 인증 서비스
141
+
91
142
  Returns:
92
- User: 현재 사용자
93
-
143
+ Dict[str, Any]: 사용자 정보
144
+
94
145
  Raises:
95
- CustomException: 인증 실패 시 예외
146
+ HTTPException: 인증 실패 시
96
147
  """
148
+ settings = get_settings()
149
+
150
+ # Authorization 헤더 검증
151
+ authorization = request.headers.get("Authorization")
152
+ if not authorization or not authorization.startswith("Bearer "):
153
+ raise HTTPException(
154
+ status_code=status.HTTP_401_UNAUTHORIZED,
155
+ detail="Not authenticated",
156
+ headers={"WWW-Authenticate": "Bearer"}
157
+ )
158
+
159
+ token = authorization.split(" ")[1]
160
+
97
161
  try:
98
- settings = get_settings()
162
+ # JWT 토큰 디코딩
99
163
  payload = jwt.decode(
100
164
  token,
101
- settings.JWT_SECRET,
102
- algorithms=[settings.JWT_ALGORITHM],
103
- audience=settings.TOKEN_AUDIENCE
165
+ settings.jwt_secret,
166
+ algorithms=[settings.jwt_algorithm],
167
+ issuer=settings.token_issuer,
168
+ audience=settings.token_audience
104
169
  )
105
- user_ulid = payload.get("sub")
170
+
171
+ # 토큰 타입 검증
172
+ token_type = payload.get("token_type")
173
+ if token_type != "access":
174
+ raise HTTPException(
175
+ status_code=status.HTTP_401_UNAUTHORIZED,
176
+ detail="Invalid token type",
177
+ headers={"WWW-Authenticate": "Bearer"}
178
+ )
179
+
180
+ # 사용자 조회
181
+ user_ulid = payload.get("user_ulid")
106
182
  if not user_ulid:
107
- raise CustomException(
108
- ErrorCode.INVALID_TOKEN,
109
- source_function="dependencies.py / get_current_user"
183
+ raise HTTPException(
184
+ status_code=status.HTTP_401_UNAUTHORIZED,
185
+ detail="Invalid token payload",
186
+ headers={"WWW-Authenticate": "Bearer"}
110
187
  )
111
- except JWTError:
112
- raise CustomException(
113
- ErrorCode.INVALID_TOKEN,
114
- detail=token[:10] + "...",
115
- source_function="dependencies.py / get_current_user"
116
- )
117
-
118
- try:
119
- repository_class, _ = service_registry.get("user")
120
- user_repo = repository_class(db_service)
121
- user = await user_repo.get_user(user_ulid, by="ulid")
122
- except ValueError:
123
- raise CustomException(
124
- ErrorCode.SERVICE_NOT_REGISTERED,
125
- detail="User service is not registered",
126
- source_function="dependencies.py / get_current_user"
127
- )
128
-
129
- if not user:
130
- raise CustomException(
131
- ErrorCode.USER_NOT_FOUND,
132
- source_function="dependencies.py / get_current_user"
188
+
189
+ user = await auth_service.get_by_ulid(user_ulid)
190
+ if not user:
191
+ raise HTTPException(
192
+ status_code=status.HTTP_401_UNAUTHORIZED,
193
+ detail="User not found",
194
+ headers={"WWW-Authenticate": "Bearer"}
195
+ )
196
+
197
+ return user
198
+
199
+ except JWTError as e:
200
+ raise HTTPException(
201
+ status_code=status.HTTP_401_UNAUTHORIZED,
202
+ detail=str(e),
203
+ headers={"WWW-Authenticate": "Bearer"}
133
204
  )
134
-
135
- return user
205
+ except HTTPException as e:
206
+ raise e
207
+ except Exception as e:
208
+ logging.error(f"Error in get_current_user: {str(e)}")
209
+ raise HTTPException(
210
+ status_code=status.HTTP_401_UNAUTHORIZED,
211
+ detail="Authentication failed",
212
+ headers={"WWW-Authenticate": "Bearer"}
213
+ )