aiteamutils 0.2.32__py3-none-any.whl → 0.2.33__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- aiteamutils/config.py +7 -2
- aiteamutils/database.py +16 -4
- aiteamutils/dependencies.py +12 -4
- aiteamutils/security.py +58 -23
- aiteamutils/version.py +1 -1
- {aiteamutils-0.2.32.dist-info → aiteamutils-0.2.33.dist-info}/METADATA +1 -1
- {aiteamutils-0.2.32.dist-info → aiteamutils-0.2.33.dist-info}/RECORD +8 -8
- {aiteamutils-0.2.32.dist-info → aiteamutils-0.2.33.dist-info}/WHEEL +0 -0
aiteamutils/config.py
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
"""설정 모듈."""
|
2
2
|
from typing import Union
|
3
3
|
from .database import init_database_service
|
4
|
+
from .exceptions import CustomException, ErrorCode
|
4
5
|
|
5
6
|
class Settings:
|
6
7
|
"""기본 설정 클래스"""
|
@@ -74,8 +75,12 @@ def get_settings() -> Settings:
|
|
74
75
|
Settings: 설정 객체
|
75
76
|
|
76
77
|
Raises:
|
77
|
-
|
78
|
+
CustomException: 설정이 초기화되지 않은 경우
|
78
79
|
"""
|
79
80
|
if _settings is None:
|
80
|
-
raise
|
81
|
+
raise CustomException(
|
82
|
+
ErrorCode.INTERNAL_ERROR,
|
83
|
+
detail="settings",
|
84
|
+
source_function="get_settings"
|
85
|
+
)
|
81
86
|
return _settings
|
aiteamutils/database.py
CHANGED
@@ -26,10 +26,14 @@ def get_database_service() -> 'DatabaseService':
|
|
26
26
|
DatabaseService: DatabaseService 인스턴스
|
27
27
|
|
28
28
|
Raises:
|
29
|
-
|
29
|
+
CustomException: DatabaseService가 초기화되지 않은 경우
|
30
30
|
"""
|
31
31
|
if _database_service is None:
|
32
|
-
raise
|
32
|
+
raise CustomException(
|
33
|
+
ErrorCode.DB_CONNECTION_ERROR,
|
34
|
+
detail="database_service",
|
35
|
+
source_function="get_database_service"
|
36
|
+
)
|
33
37
|
return _database_service
|
34
38
|
|
35
39
|
async def get_db() -> AsyncGenerator[AsyncSession, None]:
|
@@ -100,13 +104,21 @@ class DatabaseService:
|
|
100
104
|
)
|
101
105
|
self.db = session
|
102
106
|
else:
|
103
|
-
raise
|
107
|
+
raise CustomException(
|
108
|
+
ErrorCode.DB_CONNECTION_ERROR,
|
109
|
+
detail="db_url|session",
|
110
|
+
source_function="DatabaseService.__init__"
|
111
|
+
)
|
104
112
|
|
105
113
|
@asynccontextmanager
|
106
114
|
async def get_session(self) -> AsyncGenerator[AsyncSession, None]:
|
107
115
|
"""데이터베이스 세션을 생성하고 반환하는 비동기 컨텍스트 매니저."""
|
108
116
|
if self.session_factory is None:
|
109
|
-
raise
|
117
|
+
raise CustomException(
|
118
|
+
ErrorCode.DB_CONNECTION_ERROR,
|
119
|
+
detail="session_factory",
|
120
|
+
source_function="DatabaseService.get_session"
|
121
|
+
)
|
110
122
|
|
111
123
|
async with self.session_factory() as session:
|
112
124
|
try:
|
aiteamutils/dependencies.py
CHANGED
@@ -26,10 +26,14 @@ class ServiceRegistry:
|
|
26
26
|
service_class (Type): Service 클래스
|
27
27
|
|
28
28
|
Raises:
|
29
|
-
|
29
|
+
CustomException: 이미 등록된 서비스인 경우
|
30
30
|
"""
|
31
31
|
if name in self._services:
|
32
|
-
raise
|
32
|
+
raise CustomException(
|
33
|
+
ErrorCode.INTERNAL_ERROR,
|
34
|
+
detail=f"service|{name}",
|
35
|
+
source_function="ServiceRegistry.register"
|
36
|
+
)
|
33
37
|
self._services[name] = (repository_class, service_class)
|
34
38
|
|
35
39
|
def get(self, name: str) -> Tuple[Type, Type]:
|
@@ -42,10 +46,14 @@ class ServiceRegistry:
|
|
42
46
|
Tuple[Type, Type]: (Repository 클래스, Service 클래스) 튜플
|
43
47
|
|
44
48
|
Raises:
|
45
|
-
|
49
|
+
CustomException: 등록되지 않은 서비스인 경우
|
46
50
|
"""
|
47
51
|
if name not in self._services:
|
48
|
-
raise
|
52
|
+
raise CustomException(
|
53
|
+
ErrorCode.NOT_FOUND,
|
54
|
+
detail=f"service|{name}",
|
55
|
+
source_function="ServiceRegistry.get"
|
56
|
+
)
|
49
57
|
return self._services[name]
|
50
58
|
|
51
59
|
# ServiceRegistry 초기화
|
aiteamutils/security.py
CHANGED
@@ -157,8 +157,19 @@ def verify_password(plain_password: str, hashed_password: str) -> bool:
|
|
157
157
|
|
158
158
|
Returns:
|
159
159
|
bool: 비밀번호 일치 여부
|
160
|
+
|
161
|
+
Raises:
|
162
|
+
CustomException: 비밀번호 검증 실패 시
|
160
163
|
"""
|
161
|
-
|
164
|
+
try:
|
165
|
+
return pwd_context.verify(plain_password, hashed_password)
|
166
|
+
except Exception as e:
|
167
|
+
raise CustomException(
|
168
|
+
ErrorCode.INVALID_PASSWORD,
|
169
|
+
detail=plain_password,
|
170
|
+
source_function="security.verify_password",
|
171
|
+
original_error=e
|
172
|
+
)
|
162
173
|
|
163
174
|
def hash_password(password: str) -> str:
|
164
175
|
"""비밀번호를 해시화합니다.
|
@@ -168,8 +179,19 @@ def hash_password(password: str) -> str:
|
|
168
179
|
|
169
180
|
Returns:
|
170
181
|
str: 해시된 비밀번호
|
182
|
+
|
183
|
+
Raises:
|
184
|
+
CustomException: 비밀번호 해시화 실패 시
|
171
185
|
"""
|
172
|
-
|
186
|
+
try:
|
187
|
+
return pwd_context.hash(password)
|
188
|
+
except Exception as e:
|
189
|
+
raise CustomException(
|
190
|
+
ErrorCode.INTERNAL_ERROR,
|
191
|
+
detail=password,
|
192
|
+
source_function="security.hash_password",
|
193
|
+
original_error=e
|
194
|
+
)
|
173
195
|
|
174
196
|
def rate_limit(
|
175
197
|
max_requests: int,
|
@@ -288,10 +310,10 @@ async def create_jwt_token(
|
|
288
310
|
required_fields = {"username", "ulid"}
|
289
311
|
missing_fields = required_fields - set(user_data.keys())
|
290
312
|
if missing_fields:
|
291
|
-
raise
|
292
|
-
|
293
|
-
|
294
|
-
|
313
|
+
raise CustomException(
|
314
|
+
ErrorCode.REQUIRED_FIELD_MISSING,
|
315
|
+
detail="|".join(missing_fields),
|
316
|
+
source_function="security.create_jwt_token"
|
295
317
|
)
|
296
318
|
|
297
319
|
if token_type == "access":
|
@@ -337,10 +359,10 @@ async def create_jwt_token(
|
|
337
359
|
algorithm=settings.JWT_ALGORITHM
|
338
360
|
)
|
339
361
|
except Exception as e:
|
340
|
-
raise
|
341
|
-
|
342
|
-
|
343
|
-
|
362
|
+
raise CustomException(
|
363
|
+
ErrorCode.INTERNAL_ERROR,
|
364
|
+
detail=f"token|{token_type}",
|
365
|
+
source_function="security.create_jwt_token",
|
344
366
|
original_error=e
|
345
367
|
)
|
346
368
|
|
@@ -362,13 +384,13 @@ async def create_jwt_token(
|
|
362
384
|
|
363
385
|
return token
|
364
386
|
|
365
|
-
except
|
387
|
+
except CustomException as e:
|
366
388
|
raise e
|
367
389
|
except Exception as e:
|
368
|
-
raise
|
390
|
+
raise CustomException(
|
369
391
|
ErrorCode.INTERNAL_ERROR,
|
370
392
|
detail=str(e),
|
371
|
-
source_function="create_jwt_token",
|
393
|
+
source_function="security.create_jwt_token",
|
372
394
|
original_error=e
|
373
395
|
)
|
374
396
|
|
@@ -376,7 +398,18 @@ async def verify_jwt_token(
|
|
376
398
|
token: str,
|
377
399
|
expected_type: Optional[Literal["access", "refresh"]] = None
|
378
400
|
) -> Dict[str, Any]:
|
379
|
-
"""JWT 토큰을 검증합니다.
|
401
|
+
"""JWT 토큰을 검증합니다.
|
402
|
+
|
403
|
+
Args:
|
404
|
+
token: 검증할 JWT 토큰
|
405
|
+
expected_type: 예상되는 토큰 타입
|
406
|
+
|
407
|
+
Returns:
|
408
|
+
Dict[str, Any]: 토큰 페이로드
|
409
|
+
|
410
|
+
Raises:
|
411
|
+
CustomException: 토큰 검증 실패 시
|
412
|
+
"""
|
380
413
|
try:
|
381
414
|
settings = get_settings()
|
382
415
|
# 토큰 디코딩
|
@@ -390,25 +423,27 @@ async def verify_jwt_token(
|
|
390
423
|
|
391
424
|
# 토큰 타입 검증 (expected_type이 주어진 경우에만)
|
392
425
|
if expected_type and payload.get("token_type") != expected_type:
|
393
|
-
raise
|
394
|
-
|
395
|
-
|
426
|
+
raise CustomException(
|
427
|
+
ErrorCode.INVALID_TOKEN,
|
428
|
+
detail=f"token|{expected_type}|{payload.get('token_type')}",
|
429
|
+
source_function="security.verify_jwt_token"
|
396
430
|
)
|
397
431
|
|
398
432
|
return payload
|
399
433
|
|
400
434
|
except JWTError as e:
|
401
|
-
raise
|
402
|
-
|
403
|
-
|
435
|
+
raise CustomException(
|
436
|
+
ErrorCode.INVALID_TOKEN,
|
437
|
+
detail=token[:10] + "...",
|
438
|
+
source_function="security.verify_jwt_token",
|
404
439
|
original_error=e
|
405
440
|
)
|
406
|
-
except
|
441
|
+
except CustomException as e:
|
407
442
|
raise e
|
408
443
|
except Exception as e:
|
409
|
-
raise
|
444
|
+
raise CustomException(
|
410
445
|
ErrorCode.INTERNAL_ERROR,
|
411
446
|
detail=str(e),
|
412
|
-
source_function="verify_jwt_token",
|
447
|
+
source_function="security.verify_jwt_token",
|
413
448
|
original_error=e
|
414
449
|
)
|
aiteamutils/version.py
CHANGED
@@ -1,2 +1,2 @@
|
|
1
1
|
"""버전 정보"""
|
2
|
-
__version__ = "0.2.
|
2
|
+
__version__ = "0.2.33"
|
@@ -3,14 +3,14 @@ aiteamutils/base_model.py,sha256=ODEnjvUVoxQ1RPCfq8-uZTfTADIA4c7Z3E6G4EVsSX0,270
|
|
3
3
|
aiteamutils/base_repository.py,sha256=qdwQ7Sj2fUqxpDg6cWM48n_QbwPK_VUlG9zTSem8iCk,18968
|
4
4
|
aiteamutils/base_service.py,sha256=E4dHGE0DvhmRyFplh46SwKJOSF_nUL7OAsCkf_ZJF_8,24733
|
5
5
|
aiteamutils/cache.py,sha256=tr0Yn8VPYA9QHiKCUzciVlQ2J1RAwNo2K9lGMH4rY3s,1334
|
6
|
-
aiteamutils/config.py,sha256=
|
7
|
-
aiteamutils/database.py,sha256=
|
8
|
-
aiteamutils/dependencies.py,sha256=
|
6
|
+
aiteamutils/config.py,sha256=kFKMeIx1KcuEwwx4VjZdCgoTOHCkG3ySYVJ0G6cvMoA,2849
|
7
|
+
aiteamutils/database.py,sha256=CaH73g8PPNcmLCz4Xr0DBtNSrLcpRlyt0F1zO5Tigfo,33811
|
8
|
+
aiteamutils/dependencies.py,sha256=GgN8sFM7qGNmOfpLaFrhuHOgPlBVXWrEgxv29jxjEgI,4226
|
9
9
|
aiteamutils/enums.py,sha256=ipZi6k_QD5-3QV7Yzv7bnL0MjDz-vqfO9I5L77biMKs,632
|
10
10
|
aiteamutils/exceptions.py,sha256=YV-ISya4wQlHk4twvGo16I5r8h22-tXpn9wa-b3WwDM,15231
|
11
|
-
aiteamutils/security.py,sha256=
|
11
|
+
aiteamutils/security.py,sha256=nqAyypyiZ26s-eKv7c6YPDTK6HcqimFnoCr12Wtg1DY,14449
|
12
12
|
aiteamutils/validators.py,sha256=3N245cZFjgwtW_KzjESkizx5BBUDaJLbbxfNO4WOFZ0,7764
|
13
|
-
aiteamutils/version.py,sha256=
|
14
|
-
aiteamutils-0.2.
|
15
|
-
aiteamutils-0.2.
|
16
|
-
aiteamutils-0.2.
|
13
|
+
aiteamutils/version.py,sha256=0u62c8uMhBIqtJ4INrTiDbrTgFVL2CBQ_nfjWnhrldU,42
|
14
|
+
aiteamutils-0.2.33.dist-info/METADATA,sha256=DUbbkj6hQ_0C7ODwi8AuQJ1W1BYAMF8_45epV7wl27I,1718
|
15
|
+
aiteamutils-0.2.33.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
16
|
+
aiteamutils-0.2.33.dist-info/RECORD,,
|
File without changes
|