sharedkernel 2.5.3__tar.gz → 2.5.6__tar.gz
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.
- {sharedkernel-2.5.3 → sharedkernel-2.5.6}/PKG-INFO +8 -1
- {sharedkernel-2.5.3 → sharedkernel-2.5.6}/README.md +6 -0
- {sharedkernel-2.5.3 → sharedkernel-2.5.6}/setup.py +2 -1
- sharedkernel-2.5.6/sharedkernel/config.py +34 -0
- sharedkernel-2.5.6/sharedkernel/database/distributed_cache.py +138 -0
- sharedkernel-2.5.6/sharedkernel/enum/__init__.py +2 -0
- {sharedkernel-2.5.3 → sharedkernel-2.5.6}/sharedkernel/enum/error_code.py +1 -0
- sharedkernel-2.5.6/sharedkernel/enum/redis_mode_enum.py +8 -0
- sharedkernel-2.5.6/sharedkernel/ip_session_service.py +98 -0
- {sharedkernel-2.5.3 → sharedkernel-2.5.6}/sharedkernel/logger/log_decorator.py +2 -4
- {sharedkernel-2.5.3 → sharedkernel-2.5.6}/sharedkernel/logger/log_middlewares.py +3 -6
- {sharedkernel-2.5.3 → sharedkernel-2.5.6}/sharedkernel/logger/logger_service.py +10 -16
- {sharedkernel-2.5.3 → sharedkernel-2.5.6}/sharedkernel.egg-info/PKG-INFO +8 -1
- {sharedkernel-2.5.3 → sharedkernel-2.5.6}/sharedkernel.egg-info/SOURCES.txt +4 -0
- {sharedkernel-2.5.3 → sharedkernel-2.5.6}/sharedkernel.egg-info/requires.txt +1 -0
- sharedkernel-2.5.3/sharedkernel/enum/__init__.py +0 -1
- {sharedkernel-2.5.3 → sharedkernel-2.5.6}/setup.cfg +0 -0
- {sharedkernel-2.5.3 → sharedkernel-2.5.6}/sharedkernel/common.py +0 -0
- {sharedkernel-2.5.3 → sharedkernel-2.5.6}/sharedkernel/data_format_converter.py +0 -0
- {sharedkernel-2.5.3 → sharedkernel-2.5.6}/sharedkernel/database/__init__.py +0 -0
- {sharedkernel-2.5.3 → sharedkernel-2.5.6}/sharedkernel/database/audit_model.py +0 -0
- {sharedkernel-2.5.3 → sharedkernel-2.5.6}/sharedkernel/database/mongo_generic_audit_repository.py +0 -0
- {sharedkernel-2.5.3 → sharedkernel-2.5.6}/sharedkernel/database/mongo_generic_repository.py +0 -0
- {sharedkernel-2.5.3 → sharedkernel-2.5.6}/sharedkernel/database/pagination_response_dto.py +0 -0
- {sharedkernel-2.5.3 → sharedkernel-2.5.6}/sharedkernel/date_converter.py +0 -0
- {sharedkernel-2.5.3 → sharedkernel-2.5.6}/sharedkernel/diff_utils.py +0 -0
- {sharedkernel-2.5.3 → sharedkernel-2.5.6}/sharedkernel/enum/sort_order.py +0 -0
- {sharedkernel-2.5.3 → sharedkernel-2.5.6}/sharedkernel/exception/__init__.py +0 -0
- {sharedkernel-2.5.3 → sharedkernel-2.5.6}/sharedkernel/exception/exception.py +0 -0
- {sharedkernel-2.5.3 → sharedkernel-2.5.6}/sharedkernel/exception/exception_handlers.py +0 -0
- {sharedkernel-2.5.3 → sharedkernel-2.5.6}/sharedkernel/file_validation.py +0 -0
- {sharedkernel-2.5.3 → sharedkernel-2.5.6}/sharedkernel/jwt_service.py +0 -0
- {sharedkernel-2.5.3 → sharedkernel-2.5.6}/sharedkernel/logger/log_dto.py +0 -0
- {sharedkernel-2.5.3 → sharedkernel-2.5.6}/sharedkernel/logger/log_enums.py +0 -0
- {sharedkernel-2.5.3 → sharedkernel-2.5.6}/sharedkernel/logger/log_info.py +0 -0
- {sharedkernel-2.5.3 → sharedkernel-2.5.6}/sharedkernel/multipart_upload.py +0 -0
- {sharedkernel-2.5.3 → sharedkernel-2.5.6}/sharedkernel/normalizer/__init__.py +0 -0
- {sharedkernel-2.5.3 → sharedkernel-2.5.6}/sharedkernel/normalizer/number_normalizer.py +0 -0
- {sharedkernel-2.5.3 → sharedkernel-2.5.6}/sharedkernel/normalizer/phone_number_normalizer.py +0 -0
- {sharedkernel-2.5.3 → sharedkernel-2.5.6}/sharedkernel/normalizer/string_normalizer.py +0 -0
- {sharedkernel-2.5.3 → sharedkernel-2.5.6}/sharedkernel/objects/__init__.py +0 -0
- {sharedkernel-2.5.3 → sharedkernel-2.5.6}/sharedkernel/objects/base_document.py +0 -0
- {sharedkernel-2.5.3 → sharedkernel-2.5.6}/sharedkernel/objects/json_string_model.py +0 -0
- {sharedkernel-2.5.3 → sharedkernel-2.5.6}/sharedkernel/objects/jwt_model.py +0 -0
- {sharedkernel-2.5.3 → sharedkernel-2.5.6}/sharedkernel/objects/result.py +0 -0
- {sharedkernel-2.5.3 → sharedkernel-2.5.6}/sharedkernel/objects/user_info.py +0 -0
- {sharedkernel-2.5.3 → sharedkernel-2.5.6}/sharedkernel/regex_masking.py +0 -0
- {sharedkernel-2.5.3 → sharedkernel-2.5.6}/sharedkernel/s3_uploader.py +0 -0
- {sharedkernel-2.5.3 → sharedkernel-2.5.6}/sharedkernel/string_extentions.py +0 -0
- {sharedkernel-2.5.3 → sharedkernel-2.5.6}/sharedkernel.egg-info/dependency_links.txt +0 -0
- {sharedkernel-2.5.3 → sharedkernel-2.5.6}/sharedkernel.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sharedkernel
|
|
3
|
-
Version: 2.5.
|
|
3
|
+
Version: 2.5.6
|
|
4
4
|
Summary: sharekernel is a shared package between all python projects
|
|
5
5
|
Author: Smilinno
|
|
6
6
|
Description-Content-Type: text/markdown
|
|
@@ -20,6 +20,7 @@ Requires-Dist: markdown
|
|
|
20
20
|
Requires-Dist: beautifulsoup4
|
|
21
21
|
Requires-Dist: deepdiff
|
|
22
22
|
Requires-Dist: kombu
|
|
23
|
+
Requires-Dist: redis
|
|
23
24
|
Dynamic: author
|
|
24
25
|
Dynamic: description
|
|
25
26
|
Dynamic: description-content-type
|
|
@@ -30,6 +31,12 @@ Dynamic: summary
|
|
|
30
31
|
this is a shared kernel package
|
|
31
32
|
|
|
32
33
|
# Change Log
|
|
34
|
+
### Version 2.5.6
|
|
35
|
+
- fix bug
|
|
36
|
+
### Version 2.5.5
|
|
37
|
+
- fix bug
|
|
38
|
+
### Version 2.5.4
|
|
39
|
+
- Add Ip Session Service
|
|
33
40
|
### Version 2.5.3
|
|
34
41
|
- Fix bug
|
|
35
42
|
### Version 2.5.2
|
|
@@ -37,9 +37,10 @@ setup(
|
|
|
37
37
|
"beautifulsoup4",
|
|
38
38
|
"deepdiff",
|
|
39
39
|
"kombu",
|
|
40
|
+
"redis",
|
|
40
41
|
],
|
|
41
42
|
# *strongly* suggested for sharing
|
|
42
|
-
version="2.5.
|
|
43
|
+
version="2.5.6",
|
|
43
44
|
description="sharekernel is a shared package between all python projects",
|
|
44
45
|
long_description=long_description,
|
|
45
46
|
long_description_content_type="text/markdown",
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import os
|
|
2
|
+
|
|
3
|
+
# Log *******************************************************************************
|
|
4
|
+
LOG_ENABLE = True if os.getenv("LOG_ENABLE", "False").lower() == "true" else False
|
|
5
|
+
RABBITMQ_USER = os.getenv("RABBITMQ_USER")
|
|
6
|
+
RABBITMQ_PASSWORD = os.getenv("RABBITMQ_PASSWORD")
|
|
7
|
+
RABBITMQ_HOST = os.getenv("RABBITMQ_HOST")
|
|
8
|
+
RABBITMQ_VHOST = os.getenv("RABBITMQ_VHOST")
|
|
9
|
+
LOG_EXCHANGE = os.getenv("LOG_EXCHANGE")
|
|
10
|
+
LOG_REQUESTS_QUEUE = os.getenv("LOG_REQUESTS_QUEUE")
|
|
11
|
+
SERVICE_NAME = os.getenv("SERVICE_NAME")
|
|
12
|
+
IP_HEADER_NAME = os.getenv("IP_HEADER_NAME")
|
|
13
|
+
|
|
14
|
+
# IP Session ************************************************************************
|
|
15
|
+
IP_SESSION_AUTH_ENABLE = True if os.getenv("IP_SESSION_AUTH_ENABLE", "False").lower() == "true" else False
|
|
16
|
+
APIKEY_HEADER_NAME = os.getenv("APIKEY_HEADER_NAME")
|
|
17
|
+
WHITE_LIST_IP = os.getenv("WHITE_LIST_IP") #192.168.0.0/16, 10.0.0.0/24, 11.22.33.44
|
|
18
|
+
IP_SESSION_CACHE_DATABASE_NUMBER = os.getenv("IP_SESSION_CACHE_DATABASE_NUMBER")
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
# REDIS *****************************************************************************
|
|
22
|
+
REDIS_URL = os.getenv("REDIS_URL")
|
|
23
|
+
REDIS_PASSWORD = os.getenv("REDIS_PASSWORD")
|
|
24
|
+
REDIS_USER = os.getenv("REDIS_USER")
|
|
25
|
+
REDIS_CONNECTION_STRING = os.getenv("REDIS_CONNECTION_STRING")
|
|
26
|
+
CACHE_DATABASE_NUMBER = os.getenv("CACHE_DATABASE_NUMBER")
|
|
27
|
+
REDIS_CONNECTION_STRING = os.getenv(
|
|
28
|
+
"REDIS_CONNECTION_STRING", f"redis://{REDIS_USER}:{REDIS_PASSWORD}@{REDIS_URL}"
|
|
29
|
+
)
|
|
30
|
+
CACHE_EXPIRE_TIME_HOURS = int(os.getenv("CACHE_EXPIRE_TIME_HOURS", 24))
|
|
31
|
+
REDIS_SENTINELS = os.getenv("REDIS_SENTINELS")
|
|
32
|
+
REDIS_MASTER_NAME = os.getenv("REDIS_MASTER_NAME")
|
|
33
|
+
REDIS_CLUSTER_NODES = os.getenv("REDIS_CLUSTER_NODES")
|
|
34
|
+
REDIS_MODE = os.getenv("REDIS_MODE") # sentinel / cluster / standalone
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
from typing import Generic, List, Optional, Tuple, Type, TypeVar
|
|
2
|
+
|
|
3
|
+
import redis
|
|
4
|
+
from pydantic import BaseModel
|
|
5
|
+
from redis import Redis
|
|
6
|
+
from redis.sentinel import Sentinel
|
|
7
|
+
from redis.cluster import RedisCluster, ClusterNode
|
|
8
|
+
from sharedkernel.exception.exception import BusinessException
|
|
9
|
+
|
|
10
|
+
from sharedkernel import config
|
|
11
|
+
from sharedkernel.enum import ErrorCode, RedisMode
|
|
12
|
+
|
|
13
|
+
T = TypeVar("T", bound=BaseModel)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
from typing import List, Tuple
|
|
17
|
+
|
|
18
|
+
def parse_nodes(value: str) -> List[Tuple[str, int]]:
|
|
19
|
+
if not value:
|
|
20
|
+
return []
|
|
21
|
+
|
|
22
|
+
sentinels = []
|
|
23
|
+
for item in value.split(","):
|
|
24
|
+
item = item.strip()
|
|
25
|
+
|
|
26
|
+
if ":" in item:
|
|
27
|
+
host, port = item.rsplit(":", 1)
|
|
28
|
+
port = int(port)
|
|
29
|
+
else:
|
|
30
|
+
host = item
|
|
31
|
+
port = 6379
|
|
32
|
+
|
|
33
|
+
sentinels.append((host, port))
|
|
34
|
+
|
|
35
|
+
return sentinels
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def get_redis_client(cache_database_number=config.CACHE_DATABASE_NUMBER) -> Redis:
|
|
40
|
+
redis_mode = config.REDIS_MODE
|
|
41
|
+
if redis_mode == RedisMode.SENTINEL:
|
|
42
|
+
sentinels = parse_nodes(config.REDIS_SENTINELS)
|
|
43
|
+
|
|
44
|
+
sentinel = Sentinel(sentinels)
|
|
45
|
+
return sentinel.master_for(
|
|
46
|
+
service_name=config.REDIS_MASTER_NAME,
|
|
47
|
+
db=cache_database_number,
|
|
48
|
+
username=config.REDIS_USER,
|
|
49
|
+
password=config.REDIS_PASSWORD,
|
|
50
|
+
# decode_responses=True,
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
elif redis_mode == RedisMode.CLUSTER:
|
|
54
|
+
nodes = parse_nodes(config.REDIS_CLUSTER_NODES)
|
|
55
|
+
|
|
56
|
+
startup_nodes = [ClusterNode(host=h, port=p) for h, p in nodes]
|
|
57
|
+
|
|
58
|
+
return RedisCluster(
|
|
59
|
+
startup_nodes=startup_nodes,
|
|
60
|
+
username=config.REDIS_USER,
|
|
61
|
+
password=config.REDIS_PASSWORD,
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
else:
|
|
65
|
+
return redis.Redis.from_url(
|
|
66
|
+
url=f"{config.REDIS_CONNECTION_STRING}/{cache_database_number}"
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
class DistributedCache(Generic[T]):
|
|
72
|
+
def __init__(
|
|
73
|
+
self,
|
|
74
|
+
namespace: str,
|
|
75
|
+
model: Type[T],
|
|
76
|
+
expire_time_hour: int = config.CACHE_EXPIRE_TIME_HOURS,
|
|
77
|
+
):
|
|
78
|
+
"""
|
|
79
|
+
:param namespace: Prefix for the key
|
|
80
|
+
:param model: The Pydantic model class to store/retrieve
|
|
81
|
+
:param expire_time_hour: Time-to-live for each item in hours
|
|
82
|
+
"""
|
|
83
|
+
|
|
84
|
+
self.client = get_redis_client()
|
|
85
|
+
if not self.client.ping():
|
|
86
|
+
raise BusinessException(ErrorCode.Internal_Server)
|
|
87
|
+
|
|
88
|
+
self.namespace = namespace
|
|
89
|
+
self.model = model
|
|
90
|
+
self.expire_time = self.expire_time_to_seconds(expire_time_hour)
|
|
91
|
+
|
|
92
|
+
def expire_time_to_seconds(self, hours: int) -> int:
|
|
93
|
+
return hours * 60 * 60
|
|
94
|
+
|
|
95
|
+
def _pref(self, key: str) -> str:
|
|
96
|
+
return f"{self.namespace}:{key}"
|
|
97
|
+
|
|
98
|
+
def _serialize(self, value: T) -> str:
|
|
99
|
+
return value.model_dump_json()
|
|
100
|
+
|
|
101
|
+
def _deserialize(self, raw: bytes) -> T:
|
|
102
|
+
return self.model.model_validate_json(raw.decode())
|
|
103
|
+
|
|
104
|
+
def set(self, key: str, value: T) -> None:
|
|
105
|
+
if value is None:
|
|
106
|
+
return
|
|
107
|
+
skey = self._pref(key)
|
|
108
|
+
payload = self._serialize(value)
|
|
109
|
+
self.client.set(skey, payload, ex=self.expire_time)
|
|
110
|
+
|
|
111
|
+
def get(self, key: str) -> Optional[T]:
|
|
112
|
+
skey = self._pref(key)
|
|
113
|
+
raw = self.client.get(skey)
|
|
114
|
+
if raw is None:
|
|
115
|
+
return None
|
|
116
|
+
return self._deserialize(raw)
|
|
117
|
+
|
|
118
|
+
def get_all_ids(self) -> list[str]:
|
|
119
|
+
return [
|
|
120
|
+
raw.decode().split(":")[1]
|
|
121
|
+
for raw in self.client.scan_iter(f"{self.namespace}:*")
|
|
122
|
+
]
|
|
123
|
+
|
|
124
|
+
def exists(self, key: str) -> bool:
|
|
125
|
+
skey = self._pref(key)
|
|
126
|
+
return self.client.exists(skey)
|
|
127
|
+
|
|
128
|
+
def delete(self, key):
|
|
129
|
+
self.client.delete(key)
|
|
130
|
+
|
|
131
|
+
def delete_group(self, key_prefix: str):
|
|
132
|
+
pattern = f"{self.namespace}:{key_prefix}*"
|
|
133
|
+
for k in self.client.scan_iter(pattern):
|
|
134
|
+
self.client.delete(k)
|
|
135
|
+
|
|
136
|
+
def clear(self):
|
|
137
|
+
for k in self.client.scan_iter(f"{self.namespace}:*"):
|
|
138
|
+
self.client.delete(k)
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import ipaddress
|
|
3
|
+
from typing import List
|
|
4
|
+
from fastapi import Request
|
|
5
|
+
from sharedkernel.exception.exception import UnAuthorizedException, BusinessException
|
|
6
|
+
from sharedkernel.database.distributed_cache import get_redis_client
|
|
7
|
+
from sharedkernel.logger.log_decorator import unified_logger
|
|
8
|
+
from sharedkernel.enum import ErrorCode
|
|
9
|
+
from sharedkernel import config
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class IPSessionAuth:
|
|
13
|
+
|
|
14
|
+
def __init__(self):
|
|
15
|
+
self.redis = get_redis_client(cache_database_number=config.IP_SESSION_CACHE_DATABASE_NUMBER)
|
|
16
|
+
self.whitelist_networks = self._load_whitelist(config.WHITE_LIST_IP)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def _load_whitelist(self, raw_value):
|
|
20
|
+
if not raw_value:
|
|
21
|
+
return []
|
|
22
|
+
|
|
23
|
+
entries = [e.strip() for e in raw_value.split(",") if e.strip()]
|
|
24
|
+
|
|
25
|
+
networks = []
|
|
26
|
+
|
|
27
|
+
for entry in entries:
|
|
28
|
+
try:
|
|
29
|
+
if "/" in entry:
|
|
30
|
+
networks.append(ipaddress.ip_network(entry, strict=False))
|
|
31
|
+
|
|
32
|
+
else:
|
|
33
|
+
ip = ipaddress.ip_address(entry)
|
|
34
|
+
networks.append(ipaddress.ip_network(f"{ip}/32"))
|
|
35
|
+
|
|
36
|
+
except ValueError:
|
|
37
|
+
raise BusinessException(ErrorCode.Unsupported_IP)
|
|
38
|
+
|
|
39
|
+
return networks
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def _is_ip_allowed(self, ip_str: str) -> bool:
|
|
43
|
+
try:
|
|
44
|
+
ip = ipaddress.ip_address(ip_str)
|
|
45
|
+
except ValueError:
|
|
46
|
+
raise BusinessException(ErrorCode.Unsupported_IP)
|
|
47
|
+
|
|
48
|
+
return any(ip in net for net in self.whitelist_networks)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
@unified_logger()
|
|
52
|
+
def ip_session_auth(self, request: Request):
|
|
53
|
+
|
|
54
|
+
if not config.IP_SESSION_AUTH_ENABLE:
|
|
55
|
+
return
|
|
56
|
+
|
|
57
|
+
api_key = request.headers.get(config.APIKEY_HEADER_NAME)
|
|
58
|
+
|
|
59
|
+
if api_key:
|
|
60
|
+
return
|
|
61
|
+
|
|
62
|
+
state = getattr(request.state, "decoded_token", None)
|
|
63
|
+
if not state:
|
|
64
|
+
raise UnAuthorizedException()
|
|
65
|
+
|
|
66
|
+
nameid = state.get("nameid")
|
|
67
|
+
sessionid = state.get("sessionId")
|
|
68
|
+
|
|
69
|
+
if not nameid or not sessionid:
|
|
70
|
+
raise UnAuthorizedException()
|
|
71
|
+
|
|
72
|
+
request_ip = request.headers.get(config.IP_HEADER_NAME)
|
|
73
|
+
if not request_ip:
|
|
74
|
+
raise UnAuthorizedException()
|
|
75
|
+
|
|
76
|
+
request_ip = request_ip.strip()
|
|
77
|
+
|
|
78
|
+
if self._is_ip_allowed(request_ip):
|
|
79
|
+
return
|
|
80
|
+
|
|
81
|
+
redis_key = f"userId:{nameid}:session:{sessionid}"
|
|
82
|
+
user_data = self.redis.get(redis_key)
|
|
83
|
+
|
|
84
|
+
if not user_data:
|
|
85
|
+
raise UnAuthorizedException()
|
|
86
|
+
|
|
87
|
+
try:
|
|
88
|
+
payload = json.loads(user_data.decode())
|
|
89
|
+
except (ValueError, UnicodeDecodeError, TypeError):
|
|
90
|
+
raise UnAuthorizedException()
|
|
91
|
+
|
|
92
|
+
stored_ip = payload.get("Ip")
|
|
93
|
+
|
|
94
|
+
if not stored_ip:
|
|
95
|
+
raise UnAuthorizedException()
|
|
96
|
+
|
|
97
|
+
if stored_ip.strip() != request_ip:
|
|
98
|
+
raise UnAuthorizedException()
|
|
@@ -2,12 +2,10 @@ import inspect
|
|
|
2
2
|
from functools import wraps
|
|
3
3
|
from typing import Callable, Type
|
|
4
4
|
from asyncio import iscoroutinefunction
|
|
5
|
-
import os
|
|
6
5
|
|
|
7
6
|
from sharedkernel.logger.logger_service import LoggerService
|
|
7
|
+
from sharedkernel import config
|
|
8
8
|
|
|
9
|
-
LOG_ENABLE = True if os.getenv("LOG_ENABLE", "True").lower() == "true" else False
|
|
10
|
-
print("Log Enable" if LOG_ENABLE else "Log Disable")
|
|
11
9
|
|
|
12
10
|
logger = LoggerService()
|
|
13
11
|
|
|
@@ -37,7 +35,7 @@ def decorate_class_methods(cls: Type) -> Type:
|
|
|
37
35
|
def unified_logger(endpoint_name: str = None, class_name: str = None) -> Callable:
|
|
38
36
|
def decorator(target: Callable | Type):
|
|
39
37
|
|
|
40
|
-
if not LOG_ENABLE:
|
|
38
|
+
if not config.LOG_ENABLE:
|
|
41
39
|
return target
|
|
42
40
|
|
|
43
41
|
if inspect.isclass(target):
|
|
@@ -1,25 +1,22 @@
|
|
|
1
1
|
import uuid
|
|
2
2
|
from typing import Callable
|
|
3
|
-
import os
|
|
4
3
|
from fastapi import Request, Response
|
|
5
4
|
from starlette.middleware.base import BaseHTTPMiddleware
|
|
6
5
|
|
|
7
6
|
|
|
8
7
|
from sharedkernel.logger.log_info import log_info
|
|
9
8
|
from sharedkernel.logger.log_dto import LogDTO
|
|
10
|
-
|
|
11
|
-
IP_HEADER_NAME = os.getenv("IP_HEADER_NAME", "true-client-ip")
|
|
12
|
-
LOG_ENABLE = True if os.getenv("LOG_ENABLE", "True").lower() == "true" else False
|
|
9
|
+
from sharedkernel import config
|
|
13
10
|
|
|
14
11
|
class LogMiddleware(BaseHTTPMiddleware):
|
|
15
12
|
|
|
16
13
|
async def dispatch(self, request: Request, call_next: Callable) -> Response:
|
|
17
14
|
|
|
18
|
-
if not LOG_ENABLE:
|
|
15
|
+
if not config.LOG_ENABLE:
|
|
19
16
|
return await call_next(request)
|
|
20
17
|
|
|
21
18
|
correlation_id = request.headers.get("x-request-id") or uuid.uuid4().hex
|
|
22
|
-
ip_address = request.headers.get(IP_HEADER_NAME)
|
|
19
|
+
ip_address = request.headers.get(config.IP_HEADER_NAME)
|
|
23
20
|
log_obj = LogDTO(
|
|
24
21
|
CorrelationId=correlation_id,
|
|
25
22
|
RequestPath=request.url.path,
|
|
@@ -4,7 +4,6 @@ from pydantic import BaseModel
|
|
|
4
4
|
from typing import Optional
|
|
5
5
|
import datetime
|
|
6
6
|
import uuid
|
|
7
|
-
import os
|
|
8
7
|
|
|
9
8
|
from sharedkernel.logger.log_info import log_info
|
|
10
9
|
from sharedkernel.enum import ErrorCode
|
|
@@ -12,26 +11,19 @@ from sharedkernel.objects.user_info import current_user_info
|
|
|
12
11
|
from sharedkernel.exception.exception import BusinessException
|
|
13
12
|
from sharedkernel.logger.log_dto import LogDTO
|
|
14
13
|
from sharedkernel.logger.log_enums import LogEventTypeEnum, LogLevelEnum
|
|
14
|
+
from sharedkernel import config
|
|
15
15
|
|
|
16
|
-
RABBITMQ_USER = os.getenv("RABBITMQ_USER")
|
|
17
|
-
RABBITMQ_PASSWORD = os.getenv("RABBITMQ_PASSWORD")
|
|
18
|
-
RABBITMQ_HOST = os.getenv("RABBITMQ_HOST")
|
|
19
|
-
RABBITMQ_VHOST = os.getenv("RABBITMQ_VHOST")
|
|
20
|
-
LOG_EXCHANGE = os.getenv("LOG_EXCHANGE")
|
|
21
|
-
LOG_REQUESTS_QUEUE = os.getenv("LOG_REQUESTS_QUEUE")
|
|
22
|
-
SERVICE_NAME = os.getenv("SERVICE_NAME")
|
|
23
|
-
IP_HEADER_NAME = os.getenv("IP_HEADER_NAME", "IP_HEADER_NAME")
|
|
24
16
|
|
|
25
17
|
rabbitmq_url = (
|
|
26
|
-
f"amqp://{RABBITMQ_USER}:{RABBITMQ_PASSWORD}"
|
|
27
|
-
f"@{RABBITMQ_HOST}/{RABBITMQ_VHOST}"
|
|
18
|
+
f"amqp://{config.RABBITMQ_USER}:{config.RABBITMQ_PASSWORD}"
|
|
19
|
+
f"@{config.RABBITMQ_HOST}/{config.RABBITMQ_VHOST}"
|
|
28
20
|
)
|
|
29
21
|
|
|
30
22
|
class LoggerService:
|
|
31
23
|
def __init__(self):
|
|
32
24
|
self._connection: Optional[Connection] = None
|
|
33
25
|
self._producer: Optional[Producer] = None
|
|
34
|
-
self._exchange = Exchange(LOG_EXCHANGE, type="topic")
|
|
26
|
+
self._exchange = Exchange(config.LOG_EXCHANGE, type="topic")
|
|
35
27
|
|
|
36
28
|
|
|
37
29
|
def _ensure_connection(self):
|
|
@@ -47,10 +39,10 @@ class LoggerService:
|
|
|
47
39
|
self._producer.publish(
|
|
48
40
|
body=log.model_dump_json(),
|
|
49
41
|
exchange=self._exchange,
|
|
50
|
-
routing_key=LOG_REQUESTS_QUEUE,
|
|
42
|
+
routing_key=config.LOG_REQUESTS_QUEUE,
|
|
51
43
|
headers={
|
|
52
44
|
"cap-msg-id": str(uuid.uuid4()),
|
|
53
|
-
"cap-msg-name": LOG_REQUESTS_QUEUE,
|
|
45
|
+
"cap-msg-name": config.LOG_REQUESTS_QUEUE,
|
|
54
46
|
},
|
|
55
47
|
content_type="application/json",
|
|
56
48
|
retry=True
|
|
@@ -70,7 +62,7 @@ class LoggerService:
|
|
|
70
62
|
user = current_user_info.get(None)
|
|
71
63
|
if user:
|
|
72
64
|
log.UserId = user.nameid
|
|
73
|
-
log.ServiceName = SERVICE_NAME
|
|
65
|
+
log.ServiceName = config.SERVICE_NAME
|
|
74
66
|
log.MethodName = method_name
|
|
75
67
|
log.Description = endpoint_name
|
|
76
68
|
log.IsSuccess = is_success
|
|
@@ -141,8 +133,10 @@ class LoggerService:
|
|
|
141
133
|
|
|
142
134
|
@staticmethod
|
|
143
135
|
def websocket_handler(websocket: WebSocket):
|
|
136
|
+
if not config.LOG_ENABLE:
|
|
137
|
+
return
|
|
144
138
|
correlation_id = websocket.headers.get("x-request-id") or uuid.uuid4().hex
|
|
145
|
-
ip_address = websocket.headers.get(IP_HEADER_NAME)
|
|
139
|
+
ip_address = websocket.headers.get(config.IP_HEADER_NAME)
|
|
146
140
|
|
|
147
141
|
log_obj = LogDTO(
|
|
148
142
|
CorrelationId=correlation_id,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sharedkernel
|
|
3
|
-
Version: 2.5.
|
|
3
|
+
Version: 2.5.6
|
|
4
4
|
Summary: sharekernel is a shared package between all python projects
|
|
5
5
|
Author: Smilinno
|
|
6
6
|
Description-Content-Type: text/markdown
|
|
@@ -20,6 +20,7 @@ Requires-Dist: markdown
|
|
|
20
20
|
Requires-Dist: beautifulsoup4
|
|
21
21
|
Requires-Dist: deepdiff
|
|
22
22
|
Requires-Dist: kombu
|
|
23
|
+
Requires-Dist: redis
|
|
23
24
|
Dynamic: author
|
|
24
25
|
Dynamic: description
|
|
25
26
|
Dynamic: description-content-type
|
|
@@ -30,6 +31,12 @@ Dynamic: summary
|
|
|
30
31
|
this is a shared kernel package
|
|
31
32
|
|
|
32
33
|
# Change Log
|
|
34
|
+
### Version 2.5.6
|
|
35
|
+
- fix bug
|
|
36
|
+
### Version 2.5.5
|
|
37
|
+
- fix bug
|
|
38
|
+
### Version 2.5.4
|
|
39
|
+
- Add Ip Session Service
|
|
33
40
|
### Version 2.5.3
|
|
34
41
|
- Fix bug
|
|
35
42
|
### Version 2.5.2
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
README.md
|
|
2
2
|
setup.py
|
|
3
3
|
sharedkernel/common.py
|
|
4
|
+
sharedkernel/config.py
|
|
4
5
|
sharedkernel/data_format_converter.py
|
|
5
6
|
sharedkernel/date_converter.py
|
|
6
7
|
sharedkernel/diff_utils.py
|
|
7
8
|
sharedkernel/file_validation.py
|
|
9
|
+
sharedkernel/ip_session_service.py
|
|
8
10
|
sharedkernel/jwt_service.py
|
|
9
11
|
sharedkernel/multipart_upload.py
|
|
10
12
|
sharedkernel/regex_masking.py
|
|
@@ -17,11 +19,13 @@ sharedkernel.egg-info/requires.txt
|
|
|
17
19
|
sharedkernel.egg-info/top_level.txt
|
|
18
20
|
sharedkernel/database/__init__.py
|
|
19
21
|
sharedkernel/database/audit_model.py
|
|
22
|
+
sharedkernel/database/distributed_cache.py
|
|
20
23
|
sharedkernel/database/mongo_generic_audit_repository.py
|
|
21
24
|
sharedkernel/database/mongo_generic_repository.py
|
|
22
25
|
sharedkernel/database/pagination_response_dto.py
|
|
23
26
|
sharedkernel/enum/__init__.py
|
|
24
27
|
sharedkernel/enum/error_code.py
|
|
28
|
+
sharedkernel/enum/redis_mode_enum.py
|
|
25
29
|
sharedkernel/enum/sort_order.py
|
|
26
30
|
sharedkernel/exception/__init__.py
|
|
27
31
|
sharedkernel/exception/exception.py
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
from .error_code import ErrorCode
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sharedkernel-2.5.3 → sharedkernel-2.5.6}/sharedkernel/database/mongo_generic_audit_repository.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sharedkernel-2.5.3 → sharedkernel-2.5.6}/sharedkernel/normalizer/phone_number_normalizer.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|