trovesuite 1.0.1__py3-none-any.whl → 1.0.31__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.
- trovesuite/__init__.py +12 -5
- trovesuite/auth/__init__.py +2 -5
- trovesuite/auth/auth_controller.py +6 -5
- trovesuite/auth/auth_read_dto.py +3 -3
- trovesuite/auth/auth_service.py +223 -80
- trovesuite/auth/auth_write_dto.py +1 -1
- trovesuite/configs/database.py +212 -58
- trovesuite/configs/settings.py +75 -132
- trovesuite/entities/health.py +4 -4
- trovesuite/notification/__init__.py +14 -0
- trovesuite/notification/notification_base.py +13 -0
- trovesuite/notification/notification_controller.py +21 -0
- trovesuite/notification/notification_read_dto.py +21 -0
- trovesuite/notification/notification_service.py +73 -0
- trovesuite/notification/notification_write_dto.py +21 -0
- trovesuite/storage/__init__.py +42 -0
- trovesuite/storage/storage_base.py +63 -0
- trovesuite/storage/storage_controller.py +198 -0
- trovesuite/storage/storage_read_dto.py +74 -0
- trovesuite/storage/storage_service.py +529 -0
- trovesuite/storage/storage_write_dto.py +70 -0
- trovesuite/utils/__init__.py +3 -1
- trovesuite/utils/helper.py +714 -5
- trovesuite/utils/templates.py +487 -0
- {trovesuite-1.0.1.dist-info → trovesuite-1.0.31.dist-info}/METADATA +184 -9
- trovesuite-1.0.31.dist-info/RECORD +34 -0
- trovesuite-1.0.1.dist-info/RECORD +0 -21
- {trovesuite-1.0.1.dist-info → trovesuite-1.0.31.dist-info}/WHEEL +0 -0
- {trovesuite-1.0.1.dist-info → trovesuite-1.0.31.dist-info}/licenses/LICENSE +0 -0
- {trovesuite-1.0.1.dist-info → trovesuite-1.0.31.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,529 @@
|
|
|
1
|
+
from datetime import datetime, timedelta
|
|
2
|
+
from typing import List
|
|
3
|
+
from azure.storage.blob import BlobServiceClient, BlobSasPermissions, generate_blob_sas
|
|
4
|
+
from azure.identity import DefaultAzureCredential, ManagedIdentityCredential
|
|
5
|
+
from azure.core.exceptions import ResourceExistsError, ResourceNotFoundError
|
|
6
|
+
from ..entities.sh_response import Respons
|
|
7
|
+
from .storage_read_dto import (
|
|
8
|
+
StorageContainerCreateServiceReadDto,
|
|
9
|
+
StorageFileUploadServiceReadDto,
|
|
10
|
+
StorageFileUpdateServiceReadDto,
|
|
11
|
+
StorageFileDeleteServiceReadDto,
|
|
12
|
+
StorageFileDownloadServiceReadDto,
|
|
13
|
+
StorageFileUrlServiceReadDto
|
|
14
|
+
)
|
|
15
|
+
from .storage_write_dto import (
|
|
16
|
+
StorageContainerCreateServiceWriteDto,
|
|
17
|
+
StorageFileUploadServiceWriteDto,
|
|
18
|
+
StorageFileUpdateServiceWriteDto,
|
|
19
|
+
StorageFileDeleteServiceWriteDto,
|
|
20
|
+
StorageFileDownloadServiceWriteDto,
|
|
21
|
+
StorageFileUrlServiceWriteDto
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class StorageService:
|
|
26
|
+
"""
|
|
27
|
+
Azure Storage service for managing blob storage operations using Managed Identity authentication.
|
|
28
|
+
Supports both system-assigned and user-assigned managed identities.
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
@staticmethod
|
|
32
|
+
def _get_credential(managed_identity_client_id: str = None):
|
|
33
|
+
"""
|
|
34
|
+
Get Azure credential for authentication.
|
|
35
|
+
- Tries Managed Identity (for Azure environments)
|
|
36
|
+
- Falls back to DefaultAzureCredential (for local dev)
|
|
37
|
+
"""
|
|
38
|
+
try:
|
|
39
|
+
if managed_identity_client_id:
|
|
40
|
+
return ManagedIdentityCredential(client_id=managed_identity_client_id)
|
|
41
|
+
else:
|
|
42
|
+
return DefaultAzureCredential(exclude_managed_identity_credential=True)
|
|
43
|
+
|
|
44
|
+
except Exception:
|
|
45
|
+
|
|
46
|
+
# Always fallback to DefaultAzureCredential
|
|
47
|
+
return DefaultAzureCredential(exclude_managed_identity_credential=True)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
@staticmethod
|
|
51
|
+
def _get_blob_service_client(storage_account_url: str, managed_identity_client_id: str = None) -> BlobServiceClient:
|
|
52
|
+
"""
|
|
53
|
+
Create a BlobServiceClient using Managed Identity.
|
|
54
|
+
|
|
55
|
+
Args:
|
|
56
|
+
storage_account_url: Azure Storage account URL (e.g., https://myaccount.blob.core.windows.net)
|
|
57
|
+
managed_identity_client_id: Optional client ID for user-assigned managed identity
|
|
58
|
+
|
|
59
|
+
Returns:
|
|
60
|
+
BlobServiceClient instance
|
|
61
|
+
"""
|
|
62
|
+
credential = StorageService._get_credential(managed_identity_client_id)
|
|
63
|
+
return BlobServiceClient(account_url=storage_account_url, credential=credential)
|
|
64
|
+
|
|
65
|
+
@staticmethod
|
|
66
|
+
def create_container(data: StorageContainerCreateServiceWriteDto) -> Respons[StorageContainerCreateServiceReadDto]:
|
|
67
|
+
"""
|
|
68
|
+
Create a new Azure Storage container.
|
|
69
|
+
|
|
70
|
+
Args:
|
|
71
|
+
data: Container creation parameters including storage account URL and container name
|
|
72
|
+
|
|
73
|
+
Returns:
|
|
74
|
+
Response object containing container details or error information
|
|
75
|
+
"""
|
|
76
|
+
try:
|
|
77
|
+
blob_service_client = StorageService._get_blob_service_client(
|
|
78
|
+
data.storage_account_url,
|
|
79
|
+
data.managed_identity_client_id
|
|
80
|
+
)
|
|
81
|
+
container_client = blob_service_client.create_container(
|
|
82
|
+
name=data.container_name,
|
|
83
|
+
public_access=data.public_access
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
container_url = container_client.url
|
|
87
|
+
|
|
88
|
+
result = StorageContainerCreateServiceReadDto(
|
|
89
|
+
container_name=data.container_name,
|
|
90
|
+
container_url=container_url
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
return Respons[StorageContainerCreateServiceReadDto](
|
|
94
|
+
detail=f"Container '{data.container_name}' created successfully",
|
|
95
|
+
error=None,
|
|
96
|
+
data=[result],
|
|
97
|
+
status_code=201,
|
|
98
|
+
success=True,
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
except ResourceExistsError:
|
|
102
|
+
return Respons[StorageContainerCreateServiceReadDto](
|
|
103
|
+
detail=f"Container '{data.container_name}' already exists",
|
|
104
|
+
error="Resource already exists",
|
|
105
|
+
data=[],
|
|
106
|
+
status_code=409,
|
|
107
|
+
success=False,
|
|
108
|
+
)
|
|
109
|
+
except Exception as e:
|
|
110
|
+
return Respons[StorageContainerCreateServiceReadDto](
|
|
111
|
+
detail="Failed to create container",
|
|
112
|
+
error=str(e),
|
|
113
|
+
data=[],
|
|
114
|
+
status_code=500,
|
|
115
|
+
success=False,
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
@staticmethod
|
|
119
|
+
def upload_file(data: StorageFileUploadServiceWriteDto) -> Respons[StorageFileUploadServiceReadDto]:
|
|
120
|
+
"""
|
|
121
|
+
Upload a file to Azure Storage blob container.
|
|
122
|
+
|
|
123
|
+
Args:
|
|
124
|
+
data: File upload parameters including storage account URL, container name,
|
|
125
|
+
file content, blob name, and optional directory path
|
|
126
|
+
|
|
127
|
+
Returns:
|
|
128
|
+
Response object containing uploaded file details or error information
|
|
129
|
+
"""
|
|
130
|
+
try:
|
|
131
|
+
# Construct the full blob name with directory path if provided
|
|
132
|
+
blob_name = data.blob_name
|
|
133
|
+
if data.directory_path:
|
|
134
|
+
# Ensure directory path ends with / and doesn't start with /
|
|
135
|
+
dir_path = data.directory_path.strip('/')
|
|
136
|
+
blob_name = f"{dir_path}/{blob_name}"
|
|
137
|
+
|
|
138
|
+
blob_service_client = StorageService._get_blob_service_client(
|
|
139
|
+
data.storage_account_url,
|
|
140
|
+
data.managed_identity_client_id
|
|
141
|
+
)
|
|
142
|
+
blob_client = blob_service_client.get_blob_client(
|
|
143
|
+
container=data.container_name,
|
|
144
|
+
blob=blob_name
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
# Upload the file
|
|
148
|
+
content_settings = None
|
|
149
|
+
if data.content_type:
|
|
150
|
+
from azure.storage.blob import ContentSettings
|
|
151
|
+
content_settings = ContentSettings(content_type=data.content_type)
|
|
152
|
+
|
|
153
|
+
blob_client.upload_blob(
|
|
154
|
+
data.file_content,
|
|
155
|
+
overwrite=False,
|
|
156
|
+
content_settings=content_settings
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
# Get blob properties
|
|
160
|
+
properties = blob_client.get_blob_properties()
|
|
161
|
+
|
|
162
|
+
result = StorageFileUploadServiceReadDto(
|
|
163
|
+
blob_name=blob_name,
|
|
164
|
+
blob_url=blob_client.url,
|
|
165
|
+
content_type=properties.content_settings.content_type if properties.content_settings else None,
|
|
166
|
+
size=properties.size
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
return Respons[StorageFileUploadServiceReadDto](
|
|
170
|
+
detail=f"File '{blob_name}' uploaded successfully",
|
|
171
|
+
error=None,
|
|
172
|
+
data=[result],
|
|
173
|
+
status_code=201,
|
|
174
|
+
success=True,
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
except ResourceExistsError:
|
|
178
|
+
return Respons[StorageFileUploadServiceReadDto](
|
|
179
|
+
detail=f"File '{blob_name}' already exists",
|
|
180
|
+
error="Resource already exists. Use update_file to modify existing files.",
|
|
181
|
+
data=[],
|
|
182
|
+
status_code=409,
|
|
183
|
+
success=False,
|
|
184
|
+
)
|
|
185
|
+
except Exception as e:
|
|
186
|
+
return Respons[StorageFileUploadServiceReadDto](
|
|
187
|
+
detail="Failed to upload file",
|
|
188
|
+
error=str(e),
|
|
189
|
+
data=[],
|
|
190
|
+
status_code=500,
|
|
191
|
+
success=False,
|
|
192
|
+
)
|
|
193
|
+
|
|
194
|
+
@staticmethod
|
|
195
|
+
def update_file(data: StorageFileUpdateServiceWriteDto) -> Respons[StorageFileUpdateServiceReadDto]:
|
|
196
|
+
"""
|
|
197
|
+
Update an existing file in Azure Storage blob container.
|
|
198
|
+
|
|
199
|
+
Args:
|
|
200
|
+
data: File update parameters including storage account URL, container name,
|
|
201
|
+
blob name, and new file content
|
|
202
|
+
|
|
203
|
+
Returns:
|
|
204
|
+
Response object containing updated file details or error information
|
|
205
|
+
"""
|
|
206
|
+
try:
|
|
207
|
+
blob_service_client = StorageService._get_blob_service_client(
|
|
208
|
+
data.storage_account_url,
|
|
209
|
+
data.managed_identity_client_id
|
|
210
|
+
)
|
|
211
|
+
blob_client = blob_service_client.get_blob_client(
|
|
212
|
+
container=data.container_name,
|
|
213
|
+
blob=data.blob_name
|
|
214
|
+
)
|
|
215
|
+
|
|
216
|
+
# Upload with overwrite=True to update
|
|
217
|
+
content_settings = None
|
|
218
|
+
if data.content_type:
|
|
219
|
+
from azure.storage.blob import ContentSettings
|
|
220
|
+
content_settings = ContentSettings(content_type=data.content_type)
|
|
221
|
+
|
|
222
|
+
blob_client.upload_blob(
|
|
223
|
+
data.file_content,
|
|
224
|
+
overwrite=True,
|
|
225
|
+
content_settings=content_settings
|
|
226
|
+
)
|
|
227
|
+
|
|
228
|
+
# Get updated properties
|
|
229
|
+
properties = blob_client.get_blob_properties()
|
|
230
|
+
|
|
231
|
+
result = StorageFileUpdateServiceReadDto(
|
|
232
|
+
blob_name=data.blob_name,
|
|
233
|
+
blob_url=blob_client.url,
|
|
234
|
+
updated_at=properties.last_modified.isoformat() if properties.last_modified else None
|
|
235
|
+
)
|
|
236
|
+
|
|
237
|
+
return Respons[StorageFileUpdateServiceReadDto](
|
|
238
|
+
detail=f"File '{data.blob_name}' updated successfully",
|
|
239
|
+
error=None,
|
|
240
|
+
data=[result],
|
|
241
|
+
status_code=200,
|
|
242
|
+
success=True,
|
|
243
|
+
)
|
|
244
|
+
|
|
245
|
+
except ResourceNotFoundError:
|
|
246
|
+
return Respons[StorageFileUpdateServiceReadDto](
|
|
247
|
+
detail=f"File '{data.blob_name}' not found",
|
|
248
|
+
error="Resource not found. Use upload_file to create new files.",
|
|
249
|
+
data=[],
|
|
250
|
+
status_code=404,
|
|
251
|
+
success=False,
|
|
252
|
+
)
|
|
253
|
+
except Exception as e:
|
|
254
|
+
return Respons[StorageFileUpdateServiceReadDto](
|
|
255
|
+
detail="Failed to update file",
|
|
256
|
+
error=str(e),
|
|
257
|
+
data=[],
|
|
258
|
+
status_code=500,
|
|
259
|
+
success=False,
|
|
260
|
+
)
|
|
261
|
+
|
|
262
|
+
@staticmethod
|
|
263
|
+
def delete_file(data: StorageFileDeleteServiceWriteDto) -> Respons[StorageFileDeleteServiceReadDto]:
|
|
264
|
+
"""
|
|
265
|
+
Delete a file from Azure Storage blob container.
|
|
266
|
+
|
|
267
|
+
Args:
|
|
268
|
+
data: File delete parameters including storage account URL, container name,
|
|
269
|
+
and blob name
|
|
270
|
+
|
|
271
|
+
Returns:
|
|
272
|
+
Response object containing deletion status or error information
|
|
273
|
+
"""
|
|
274
|
+
try:
|
|
275
|
+
blob_service_client = StorageService._get_blob_service_client(
|
|
276
|
+
data.storage_account_url,
|
|
277
|
+
data.managed_identity_client_id
|
|
278
|
+
)
|
|
279
|
+
blob_client = blob_service_client.get_blob_client(
|
|
280
|
+
container=data.container_name,
|
|
281
|
+
blob=data.blob_name
|
|
282
|
+
)
|
|
283
|
+
|
|
284
|
+
# Delete the blob
|
|
285
|
+
blob_client.delete_blob()
|
|
286
|
+
|
|
287
|
+
result = StorageFileDeleteServiceReadDto(
|
|
288
|
+
blob_name=data.blob_name,
|
|
289
|
+
deleted=True
|
|
290
|
+
)
|
|
291
|
+
|
|
292
|
+
return Respons[StorageFileDeleteServiceReadDto](
|
|
293
|
+
detail=f"File '{data.blob_name}' deleted successfully",
|
|
294
|
+
error=None,
|
|
295
|
+
data=[result],
|
|
296
|
+
status_code=200,
|
|
297
|
+
success=True,
|
|
298
|
+
)
|
|
299
|
+
|
|
300
|
+
except ResourceNotFoundError:
|
|
301
|
+
return Respons[StorageFileDeleteServiceReadDto](
|
|
302
|
+
detail=f"File '{data.blob_name}' not found",
|
|
303
|
+
error="Resource not found",
|
|
304
|
+
data=[],
|
|
305
|
+
status_code=404,
|
|
306
|
+
success=False,
|
|
307
|
+
)
|
|
308
|
+
except Exception as e:
|
|
309
|
+
return Respons[StorageFileDeleteServiceReadDto](
|
|
310
|
+
detail="Failed to delete file",
|
|
311
|
+
error=str(e),
|
|
312
|
+
data=[],
|
|
313
|
+
status_code=500,
|
|
314
|
+
success=False,
|
|
315
|
+
)
|
|
316
|
+
|
|
317
|
+
@staticmethod
|
|
318
|
+
def delete_multiple_files(
|
|
319
|
+
storage_account_url: str,
|
|
320
|
+
container_name: str,
|
|
321
|
+
blob_names: List[str],
|
|
322
|
+
managed_identity_client_id: str = None
|
|
323
|
+
) -> Respons[StorageFileDeleteServiceReadDto]:
|
|
324
|
+
"""
|
|
325
|
+
Delete multiple files from Azure Storage blob container.
|
|
326
|
+
|
|
327
|
+
Args:
|
|
328
|
+
storage_account_url: Azure Storage account URL
|
|
329
|
+
container_name: Name of the container
|
|
330
|
+
blob_names: List of blob names to delete
|
|
331
|
+
managed_identity_client_id: Optional client ID for user-assigned managed identity
|
|
332
|
+
|
|
333
|
+
Returns:
|
|
334
|
+
Response object containing deletion status for all files
|
|
335
|
+
"""
|
|
336
|
+
try:
|
|
337
|
+
blob_service_client = StorageService._get_blob_service_client(
|
|
338
|
+
storage_account_url,
|
|
339
|
+
managed_identity_client_id
|
|
340
|
+
)
|
|
341
|
+
container_client = blob_service_client.get_container_client(container_name)
|
|
342
|
+
|
|
343
|
+
results = []
|
|
344
|
+
errors = []
|
|
345
|
+
|
|
346
|
+
for blob_name in blob_names:
|
|
347
|
+
try:
|
|
348
|
+
blob_client = container_client.get_blob_client(blob_name)
|
|
349
|
+
blob_client.delete_blob()
|
|
350
|
+
results.append(StorageFileDeleteServiceReadDto(
|
|
351
|
+
blob_name=blob_name,
|
|
352
|
+
deleted=True
|
|
353
|
+
))
|
|
354
|
+
except ResourceNotFoundError:
|
|
355
|
+
errors.append(f"File '{blob_name}' not found")
|
|
356
|
+
except Exception as e:
|
|
357
|
+
errors.append(f"Failed to delete '{blob_name}': {str(e)}")
|
|
358
|
+
|
|
359
|
+
if errors and not results:
|
|
360
|
+
return Respons[StorageFileDeleteServiceReadDto](
|
|
361
|
+
detail="Failed to delete any files",
|
|
362
|
+
error="; ".join(errors),
|
|
363
|
+
data=[],
|
|
364
|
+
status_code=500,
|
|
365
|
+
success=False,
|
|
366
|
+
)
|
|
367
|
+
elif errors:
|
|
368
|
+
return Respons[StorageFileDeleteServiceReadDto](
|
|
369
|
+
detail=f"Deleted {len(results)} file(s) with {len(errors)} error(s)",
|
|
370
|
+
error="; ".join(errors),
|
|
371
|
+
data=results,
|
|
372
|
+
status_code=207, # Multi-Status
|
|
373
|
+
success=True,
|
|
374
|
+
)
|
|
375
|
+
else:
|
|
376
|
+
return Respons[StorageFileDeleteServiceReadDto](
|
|
377
|
+
detail=f"Successfully deleted {len(results)} file(s)",
|
|
378
|
+
error=None,
|
|
379
|
+
data=results,
|
|
380
|
+
status_code=200,
|
|
381
|
+
success=True,
|
|
382
|
+
)
|
|
383
|
+
|
|
384
|
+
except Exception as e:
|
|
385
|
+
return Respons[StorageFileDeleteServiceReadDto](
|
|
386
|
+
detail="Failed to delete files",
|
|
387
|
+
error=str(e),
|
|
388
|
+
data=[],
|
|
389
|
+
status_code=500,
|
|
390
|
+
success=False,
|
|
391
|
+
)
|
|
392
|
+
|
|
393
|
+
@staticmethod
|
|
394
|
+
def download_file(data: StorageFileDownloadServiceWriteDto) -> Respons[StorageFileDownloadServiceReadDto]:
|
|
395
|
+
"""
|
|
396
|
+
Download a file from Azure Storage blob container.
|
|
397
|
+
|
|
398
|
+
Args:
|
|
399
|
+
data: File download parameters including storage account URL, container name,
|
|
400
|
+
and blob name
|
|
401
|
+
|
|
402
|
+
Returns:
|
|
403
|
+
Response object containing file content and metadata or error information
|
|
404
|
+
"""
|
|
405
|
+
try:
|
|
406
|
+
blob_service_client = StorageService._get_blob_service_client(
|
|
407
|
+
data.storage_account_url,
|
|
408
|
+
data.managed_identity_client_id
|
|
409
|
+
)
|
|
410
|
+
blob_client = blob_service_client.get_blob_client(
|
|
411
|
+
container=data.container_name,
|
|
412
|
+
blob=data.blob_name
|
|
413
|
+
)
|
|
414
|
+
|
|
415
|
+
# Download the blob
|
|
416
|
+
download_stream = blob_client.download_blob()
|
|
417
|
+
file_content = download_stream.readall()
|
|
418
|
+
|
|
419
|
+
# Get blob properties
|
|
420
|
+
properties = blob_client.get_blob_properties()
|
|
421
|
+
|
|
422
|
+
result = StorageFileDownloadServiceReadDto(
|
|
423
|
+
blob_name=data.blob_name,
|
|
424
|
+
content=file_content,
|
|
425
|
+
content_type=properties.content_settings.content_type if properties.content_settings else None,
|
|
426
|
+
size=properties.size
|
|
427
|
+
)
|
|
428
|
+
|
|
429
|
+
return Respons[StorageFileDownloadServiceReadDto](
|
|
430
|
+
detail=f"File '{data.blob_name}' downloaded successfully",
|
|
431
|
+
error=None,
|
|
432
|
+
data=[result],
|
|
433
|
+
status_code=200,
|
|
434
|
+
success=True,
|
|
435
|
+
)
|
|
436
|
+
|
|
437
|
+
except ResourceNotFoundError:
|
|
438
|
+
return Respons[StorageFileDownloadServiceReadDto](
|
|
439
|
+
detail=f"File '{data.blob_name}' not found",
|
|
440
|
+
error="Resource not found",
|
|
441
|
+
data=[],
|
|
442
|
+
status_code=404,
|
|
443
|
+
success=False,
|
|
444
|
+
)
|
|
445
|
+
except Exception as e:
|
|
446
|
+
return Respons[StorageFileDownloadServiceReadDto](
|
|
447
|
+
detail="Failed to download file",
|
|
448
|
+
error=str(e),
|
|
449
|
+
data=[],
|
|
450
|
+
status_code=500,
|
|
451
|
+
success=False,
|
|
452
|
+
)
|
|
453
|
+
|
|
454
|
+
@staticmethod
|
|
455
|
+
def get_file_url(data: StorageFileUrlServiceWriteDto) -> Respons[StorageFileUrlServiceReadDto]:
|
|
456
|
+
"""
|
|
457
|
+
Generate a presigned URL (SAS token) for a file in Azure Storage blob container.
|
|
458
|
+
Note: This requires the storage account to have a shared key available.
|
|
459
|
+
|
|
460
|
+
Args:
|
|
461
|
+
data: URL generation parameters including storage account URL, container name,
|
|
462
|
+
blob name, and expiry time in hours
|
|
463
|
+
|
|
464
|
+
Returns:
|
|
465
|
+
Response object containing presigned URL or error information
|
|
466
|
+
"""
|
|
467
|
+
try:
|
|
468
|
+
blob_service_client = StorageService._get_blob_service_client(
|
|
469
|
+
data.storage_account_url,
|
|
470
|
+
data.managed_identity_client_id
|
|
471
|
+
)
|
|
472
|
+
blob_client = blob_service_client.get_blob_client(
|
|
473
|
+
container=data.container_name,
|
|
474
|
+
blob=data.blob_name
|
|
475
|
+
)
|
|
476
|
+
|
|
477
|
+
# Check if blob exists
|
|
478
|
+
if not blob_client.exists():
|
|
479
|
+
return Respons[StorageFileUrlServiceReadDto](
|
|
480
|
+
detail=f"File '{data.blob_name}' not found",
|
|
481
|
+
error="Resource not found",
|
|
482
|
+
data=[],
|
|
483
|
+
status_code=404,
|
|
484
|
+
success=False,
|
|
485
|
+
)
|
|
486
|
+
|
|
487
|
+
# Generate user delegation key for SAS token (works with Managed Identity)
|
|
488
|
+
# This requires the managed identity to have "Storage Blob Delegator" role
|
|
489
|
+
delegation_key = blob_service_client.get_user_delegation_key(
|
|
490
|
+
key_start_time=datetime.utcnow(),
|
|
491
|
+
key_expiry_time=datetime.utcnow() + timedelta(hours=data.expiry_hours or 1)
|
|
492
|
+
)
|
|
493
|
+
|
|
494
|
+
# Generate SAS token using user delegation key
|
|
495
|
+
from azure.storage.blob import generate_blob_sas, BlobSasPermissions
|
|
496
|
+
sas_token = generate_blob_sas(
|
|
497
|
+
account_name=blob_service_client.account_name,
|
|
498
|
+
container_name=data.container_name,
|
|
499
|
+
blob_name=data.blob_name,
|
|
500
|
+
user_delegation_key=delegation_key,
|
|
501
|
+
permission=BlobSasPermissions(read=True),
|
|
502
|
+
expiry=datetime.utcnow() + timedelta(hours=data.expiry_hours or 1)
|
|
503
|
+
)
|
|
504
|
+
|
|
505
|
+
# Construct the full URL with SAS token
|
|
506
|
+
presigned_url = f"{blob_client.url}?{sas_token}"
|
|
507
|
+
|
|
508
|
+
result = StorageFileUrlServiceReadDto(
|
|
509
|
+
blob_name=data.blob_name,
|
|
510
|
+
presigned_url=presigned_url,
|
|
511
|
+
expires_in_hours=data.expiry_hours or 1
|
|
512
|
+
)
|
|
513
|
+
|
|
514
|
+
return Respons[StorageFileUrlServiceReadDto](
|
|
515
|
+
detail=f"Presigned URL generated for '{data.blob_name}'",
|
|
516
|
+
error=None,
|
|
517
|
+
data=[result],
|
|
518
|
+
status_code=200,
|
|
519
|
+
success=True,
|
|
520
|
+
)
|
|
521
|
+
|
|
522
|
+
except Exception as e:
|
|
523
|
+
return Respons[StorageFileUrlServiceReadDto](
|
|
524
|
+
detail="Failed to generate presigned URL",
|
|
525
|
+
error=str(e),
|
|
526
|
+
data=[],
|
|
527
|
+
status_code=500,
|
|
528
|
+
success=False,
|
|
529
|
+
)
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
from pydantic import BaseModel
|
|
2
|
+
from .storage_base import (
|
|
3
|
+
StorageConnectionBase,
|
|
4
|
+
StorageFileUploadBase,
|
|
5
|
+
StorageFileUpdateBase,
|
|
6
|
+
StorageFileDeleteBase,
|
|
7
|
+
StorageFileDownloadBase,
|
|
8
|
+
StorageFileUrlBase,
|
|
9
|
+
StorageContainerCreateBase
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
# Container Creation
|
|
14
|
+
|
|
15
|
+
class StorageContainerCreateControllerWriteDto(StorageContainerCreateBase):
|
|
16
|
+
pass
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class StorageContainerCreateServiceWriteDto(StorageContainerCreateControllerWriteDto):
|
|
20
|
+
pass
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
# File Upload
|
|
24
|
+
|
|
25
|
+
class StorageFileUploadControllerWriteDto(StorageFileUploadBase):
|
|
26
|
+
pass
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class StorageFileUploadServiceWriteDto(StorageFileUploadControllerWriteDto):
|
|
30
|
+
pass
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
# File Update
|
|
34
|
+
|
|
35
|
+
class StorageFileUpdateControllerWriteDto(StorageFileUpdateBase):
|
|
36
|
+
pass
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class StorageFileUpdateServiceWriteDto(StorageFileUpdateControllerWriteDto):
|
|
40
|
+
pass
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
# File Delete
|
|
44
|
+
|
|
45
|
+
class StorageFileDeleteControllerWriteDto(StorageFileDeleteBase):
|
|
46
|
+
pass
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class StorageFileDeleteServiceWriteDto(StorageFileDeleteControllerWriteDto):
|
|
50
|
+
pass
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
# File Download
|
|
54
|
+
|
|
55
|
+
class StorageFileDownloadControllerWriteDto(StorageFileDownloadBase):
|
|
56
|
+
pass
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
class StorageFileDownloadServiceWriteDto(StorageFileDownloadControllerWriteDto):
|
|
60
|
+
pass
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
# File URL
|
|
64
|
+
|
|
65
|
+
class StorageFileUrlControllerWriteDto(StorageFileUrlBase):
|
|
66
|
+
pass
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
class StorageFileUrlServiceWriteDto(StorageFileUrlControllerWriteDto):
|
|
70
|
+
pass
|