sharedkernel 2.2.7__tar.gz → 2.4.0__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.
Files changed (40) hide show
  1. {sharedkernel-2.2.7 → sharedkernel-2.4.0}/PKG-INFO +5 -1
  2. {sharedkernel-2.2.7 → sharedkernel-2.4.0}/README.md +4 -0
  3. {sharedkernel-2.2.7 → sharedkernel-2.4.0}/setup.py +1 -1
  4. sharedkernel-2.4.0/sharedkernel/file_validation.py +83 -0
  5. sharedkernel-2.4.0/sharedkernel/multipart_upload.py +69 -0
  6. {sharedkernel-2.2.7 → sharedkernel-2.4.0}/sharedkernel/s3_uploader.py +20 -0
  7. {sharedkernel-2.2.7 → sharedkernel-2.4.0}/sharedkernel.egg-info/PKG-INFO +5 -1
  8. {sharedkernel-2.2.7 → sharedkernel-2.4.0}/sharedkernel.egg-info/SOURCES.txt +2 -0
  9. {sharedkernel-2.2.7 → sharedkernel-2.4.0}/setup.cfg +0 -0
  10. {sharedkernel-2.2.7 → sharedkernel-2.4.0}/sharedkernel/common.py +0 -0
  11. {sharedkernel-2.2.7 → sharedkernel-2.4.0}/sharedkernel/data_format_converter.py +0 -0
  12. {sharedkernel-2.2.7 → sharedkernel-2.4.0}/sharedkernel/database/__init__.py +0 -0
  13. {sharedkernel-2.2.7 → sharedkernel-2.4.0}/sharedkernel/database/audit_model.py +0 -0
  14. {sharedkernel-2.2.7 → sharedkernel-2.4.0}/sharedkernel/database/mongo_generic_audit_repository.py +0 -0
  15. {sharedkernel-2.2.7 → sharedkernel-2.4.0}/sharedkernel/database/mongo_generic_repository.py +0 -0
  16. {sharedkernel-2.2.7 → sharedkernel-2.4.0}/sharedkernel/database/pagination_response_dto.py +0 -0
  17. {sharedkernel-2.2.7 → sharedkernel-2.4.0}/sharedkernel/date_converter.py +0 -0
  18. {sharedkernel-2.2.7 → sharedkernel-2.4.0}/sharedkernel/diff_utils.py +0 -0
  19. {sharedkernel-2.2.7 → sharedkernel-2.4.0}/sharedkernel/enum/__init__.py +0 -0
  20. {sharedkernel-2.2.7 → sharedkernel-2.4.0}/sharedkernel/enum/error_code.py +0 -0
  21. {sharedkernel-2.2.7 → sharedkernel-2.4.0}/sharedkernel/enum/sort_order.py +0 -0
  22. {sharedkernel-2.2.7 → sharedkernel-2.4.0}/sharedkernel/exception/__init__.py +0 -0
  23. {sharedkernel-2.2.7 → sharedkernel-2.4.0}/sharedkernel/exception/exception.py +0 -0
  24. {sharedkernel-2.2.7 → sharedkernel-2.4.0}/sharedkernel/exception/exception_handlers.py +0 -0
  25. {sharedkernel-2.2.7 → sharedkernel-2.4.0}/sharedkernel/jwt_service.py +0 -0
  26. {sharedkernel-2.2.7 → sharedkernel-2.4.0}/sharedkernel/normalizer/__init__.py +0 -0
  27. {sharedkernel-2.2.7 → sharedkernel-2.4.0}/sharedkernel/normalizer/number_normalizer.py +0 -0
  28. {sharedkernel-2.2.7 → sharedkernel-2.4.0}/sharedkernel/normalizer/phone_number_normalizer.py +0 -0
  29. {sharedkernel-2.2.7 → sharedkernel-2.4.0}/sharedkernel/normalizer/string_normalizer.py +0 -0
  30. {sharedkernel-2.2.7 → sharedkernel-2.4.0}/sharedkernel/objects/__init__.py +0 -0
  31. {sharedkernel-2.2.7 → sharedkernel-2.4.0}/sharedkernel/objects/base_document.py +0 -0
  32. {sharedkernel-2.2.7 → sharedkernel-2.4.0}/sharedkernel/objects/json_string_model.py +0 -0
  33. {sharedkernel-2.2.7 → sharedkernel-2.4.0}/sharedkernel/objects/jwt_model.py +0 -0
  34. {sharedkernel-2.2.7 → sharedkernel-2.4.0}/sharedkernel/objects/result.py +0 -0
  35. {sharedkernel-2.2.7 → sharedkernel-2.4.0}/sharedkernel/objects/user_info.py +0 -0
  36. {sharedkernel-2.2.7 → sharedkernel-2.4.0}/sharedkernel/regex_masking.py +0 -0
  37. {sharedkernel-2.2.7 → sharedkernel-2.4.0}/sharedkernel/string_extentions.py +0 -0
  38. {sharedkernel-2.2.7 → sharedkernel-2.4.0}/sharedkernel.egg-info/dependency_links.txt +0 -0
  39. {sharedkernel-2.2.7 → sharedkernel-2.4.0}/sharedkernel.egg-info/requires.txt +0 -0
  40. {sharedkernel-2.2.7 → sharedkernel-2.4.0}/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.2.7
3
+ Version: 2.4.0
4
4
  Summary: sharekernel is a shared package between all python projects
5
5
  Author: Smilinno
6
6
  Description-Content-Type: text/markdown
@@ -29,6 +29,10 @@ Dynamic: summary
29
29
  this is a shared kernel package
30
30
 
31
31
  # Change Log
32
+ ### Version 2.4.0
33
+ - Implement file validation
34
+ ### Version 2.3.0
35
+ - Implement multipart uploader for S3
32
36
  ### Version 2.2.7
33
37
  - add presigned url in s3 uploader
34
38
  ### Version 2.2.6
@@ -2,6 +2,10 @@
2
2
  this is a shared kernel package
3
3
 
4
4
  # Change Log
5
+ ### Version 2.4.0
6
+ - Implement file validation
7
+ ### Version 2.3.0
8
+ - Implement multipart uploader for S3
5
9
  ### Version 2.2.7
6
10
  - add presigned url in s3 uploader
7
11
  ### Version 2.2.6
@@ -37,7 +37,7 @@ setup(
37
37
  "deepdiff",
38
38
  ],
39
39
  # *strongly* suggested for sharing
40
- version="2.2.7",
40
+ version="2.4.0",
41
41
  description="sharekernel is a shared package between all python projects",
42
42
  long_description=long_description,
43
43
  long_description_content_type="text/markdown",
@@ -0,0 +1,83 @@
1
+ from functools import wraps
2
+ from fastapi import HTTPException
3
+ import magic
4
+
5
+ DEFAULT_ALLOWED_MIME_TYPES = (
6
+ "image/jpeg",
7
+ "image/png",
8
+ "image/gif",
9
+ "image/webp",
10
+ "application/pdf",
11
+ "application/msword",
12
+ "application/x-ole-storage",
13
+ "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
14
+ "application/vnd.ms-excel",
15
+ "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
16
+ "application/vnd.ms-powerpoint",
17
+ "application/vnd.openxmlformats-officedocument.presentationml.presentation",
18
+ "text/plain",
19
+ "text/csv",
20
+ "audio/mpeg",
21
+ "audio/wav",
22
+ "audio/mp3",
23
+ )
24
+
25
+
26
+ def get_mime_type(fileobj) -> str:
27
+ """
28
+ Detects the real MIME type of a file based on its content (magic bytes),
29
+ not the client-provided extension or Content-Type header.
30
+ """
31
+ try:
32
+ fileobj.seek(0)
33
+ sample = fileobj.read(2048)
34
+ fileobj.seek(0)
35
+ m = magic.Magic(mime=True)
36
+ mime = m.from_buffer(sample)
37
+ return mime or "application/octet-stream"
38
+ except Exception:
39
+ return "application/octet-stream"
40
+
41
+
42
+ def validate_file_type(fileobj, allowed_mimes=None):
43
+ """
44
+ Validates the uploaded file’s real MIME type against the allowed list.
45
+ """
46
+ allowed_mimes = allowed_mimes or DEFAULT_ALLOWED_MIME_TYPES
47
+ mime = get_mime_type(fileobj)
48
+ if mime not in allowed_mimes:
49
+ raise ValueError(f"نوع فایل مجاز نیست: {mime}")
50
+ return mime
51
+
52
+
53
+ def is_upload_file(obj):
54
+ return (
55
+ hasattr(obj, "file") and
56
+ hasattr(obj, "filename") and
57
+ callable(getattr(obj.file, "read", None)) and
58
+ callable(getattr(obj.file, "seek", None))
59
+ )
60
+
61
+
62
+ def validate_upload_file(allowed_mimes=None):
63
+ allowed_mimes = allowed_mimes or DEFAULT_ALLOWED_MIME_TYPES
64
+
65
+ def decorator(func):
66
+ @wraps(func)
67
+ async def wrapper(*args, **kwargs):
68
+ for value in list(args) + list(kwargs.values()):
69
+ if is_upload_file(value):
70
+ try:
71
+ validate_file_type(value.file, allowed_mimes)
72
+ except ValueError as e:
73
+ raise HTTPException(status_code=400, detail=str(e))
74
+ elif isinstance(value, list):
75
+ for f in value:
76
+ if is_upload_file(f):
77
+ try:
78
+ validate_file_type(f.file, allowed_mimes)
79
+ except ValueError as e:
80
+ raise HTTPException(status_code=400, detail=str(e))
81
+ return await func(*args, **kwargs)
82
+ return wrapper
83
+ return decorator
@@ -0,0 +1,69 @@
1
+ class MultipartUploadSession:
2
+ MIN_PART_SIZE = 5 * 1024 * 1024 # 5MB
3
+
4
+ def __init__(self, s3_client, bucket, object_key, acl="private"):
5
+ self.s3 = s3_client
6
+ self.bucket = bucket
7
+ self.key = object_key
8
+ self.acl = acl
9
+
10
+ self.upload_id = None
11
+ self.parts = []
12
+ self.part_number = 1
13
+ self.buffer = bytearray()
14
+
15
+ self._start()
16
+
17
+ def _start(self):
18
+ resp = self.s3.create_multipart_upload(
19
+ Bucket=self.bucket,
20
+ Key=self.key,
21
+ ACL=self.acl,
22
+ )
23
+ self.upload_id = resp["UploadId"]
24
+
25
+ def upload_chunk(self, chunk: bytes):
26
+ """
27
+ Receives small chunks (KB) and buffers them
28
+ """
29
+ self.buffer.extend(chunk)
30
+
31
+ if len(self.buffer) >= self.MIN_PART_SIZE:
32
+ self._upload_part(bytes(self.buffer))
33
+ self.buffer.clear()
34
+
35
+ def _upload_part(self, data: bytes):
36
+ resp = self.s3.upload_part(
37
+ Bucket=self.bucket,
38
+ Key=self.key,
39
+ UploadId=self.upload_id,
40
+ PartNumber=self.part_number,
41
+ Body=data,
42
+ )
43
+
44
+ self.parts.append({
45
+ "ETag": resp["ETag"],
46
+ "PartNumber": self.part_number
47
+ })
48
+ self.part_number += 1
49
+
50
+ def complete(self):
51
+ # upload remaining buffer (can be < 5MB)
52
+ if self.buffer:
53
+ self._upload_part(bytes(self.buffer))
54
+ self.buffer.clear()
55
+
56
+ self.s3.complete_multipart_upload(
57
+ Bucket=self.bucket,
58
+ Key=self.key,
59
+ UploadId=self.upload_id,
60
+ MultipartUpload={"Parts": self.parts},
61
+ )
62
+
63
+ def abort(self):
64
+ if self.upload_id:
65
+ self.s3.abort_multipart_upload(
66
+ Bucket=self.bucket,
67
+ Key=self.key,
68
+ UploadId=self.upload_id,
69
+ )
@@ -4,6 +4,7 @@ import boto3
4
4
  from io import BytesIO
5
5
  import uuid
6
6
  import os
7
+ from sharedkernel.multipart_upload import MultipartUploadSession
7
8
 
8
9
  class S3Uploader:
9
10
  def __init__(self, endpoint_url, bucket, access_key, secret_key):
@@ -82,5 +83,24 @@ class S3Uploader:
82
83
  file_obj = BytesIO(response.content)
83
84
  return self.upload_file_object(file_obj=file_obj, object_name=object_name, file_extension=file_extension, folder_name=folder_name)
84
85
 
86
+ def create_multipart_session(
87
+ self,
88
+ object_name=None,
89
+ file_extension=None,
90
+ folder_name=None,
91
+ public_read=True,
92
+ ):
93
+ object_key = self.__generate_object_name(
94
+ object_name, file_extension, folder_name
95
+ )
96
+
97
+ acl = "public-read" if public_read else "private"
98
+
99
+ return MultipartUploadSession(
100
+ s3_client=self.s3,
101
+ bucket=self.bucket,
102
+ object_key=object_key,
103
+ acl=acl,
104
+ )
85
105
 
86
106
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sharedkernel
3
- Version: 2.2.7
3
+ Version: 2.4.0
4
4
  Summary: sharekernel is a shared package between all python projects
5
5
  Author: Smilinno
6
6
  Description-Content-Type: text/markdown
@@ -29,6 +29,10 @@ Dynamic: summary
29
29
  this is a shared kernel package
30
30
 
31
31
  # Change Log
32
+ ### Version 2.4.0
33
+ - Implement file validation
34
+ ### Version 2.3.0
35
+ - Implement multipart uploader for S3
32
36
  ### Version 2.2.7
33
37
  - add presigned url in s3 uploader
34
38
  ### Version 2.2.6
@@ -4,7 +4,9 @@ sharedkernel/common.py
4
4
  sharedkernel/data_format_converter.py
5
5
  sharedkernel/date_converter.py
6
6
  sharedkernel/diff_utils.py
7
+ sharedkernel/file_validation.py
7
8
  sharedkernel/jwt_service.py
9
+ sharedkernel/multipart_upload.py
8
10
  sharedkernel/regex_masking.py
9
11
  sharedkernel/s3_uploader.py
10
12
  sharedkernel/string_extentions.py
File without changes