geek-cafe-saas-sdk 0.7.0__py3-none-any.whl → 0.7.2__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.
Potentially problematic release.
This version of geek-cafe-saas-sdk might be problematic. Click here for more details.
- geek_cafe_saas_sdk/__init__.py +1 -1
- geek_cafe_saas_sdk/domains/files/models/directory.py +42 -6
- geek_cafe_saas_sdk/domains/files/models/file.py +40 -4
- geek_cafe_saas_sdk/domains/files/models/file_share.py +33 -0
- geek_cafe_saas_sdk/domains/files/models/file_version.py +24 -0
- geek_cafe_saas_sdk/domains/files/services/directory_service.py +54 -135
- geek_cafe_saas_sdk/domains/files/services/file_share_service.py +60 -136
- geek_cafe_saas_sdk/domains/files/services/file_system_service.py +43 -104
- geek_cafe_saas_sdk/domains/files/services/file_version_service.py +57 -131
- geek_cafe_saas_sdk/domains/messaging/services/contact_thread_service.py +55 -7
- geek_cafe_saas_sdk/domains/notifications/__init__.py +18 -0
- geek_cafe_saas_sdk/domains/notifications/handlers/__init__.py +1 -0
- geek_cafe_saas_sdk/domains/notifications/handlers/create_webhook/app.py +73 -0
- geek_cafe_saas_sdk/domains/notifications/handlers/get/app.py +40 -0
- geek_cafe_saas_sdk/domains/notifications/handlers/get_preferences/app.py +34 -0
- geek_cafe_saas_sdk/domains/notifications/handlers/list/app.py +43 -0
- geek_cafe_saas_sdk/domains/notifications/handlers/list_webhooks/app.py +40 -0
- geek_cafe_saas_sdk/domains/notifications/handlers/mark_read/app.py +40 -0
- geek_cafe_saas_sdk/domains/notifications/handlers/send/app.py +83 -0
- geek_cafe_saas_sdk/domains/notifications/handlers/update_preferences/app.py +45 -0
- geek_cafe_saas_sdk/domains/notifications/models/__init__.py +16 -0
- geek_cafe_saas_sdk/domains/notifications/models/notification.py +717 -0
- geek_cafe_saas_sdk/domains/notifications/models/notification_preference.py +365 -0
- geek_cafe_saas_sdk/domains/notifications/models/webhook_subscription.py +339 -0
- geek_cafe_saas_sdk/domains/notifications/services/__init__.py +10 -0
- geek_cafe_saas_sdk/domains/notifications/services/notification_service.py +576 -0
- geek_cafe_saas_sdk/domains/payments/__init__.py +16 -0
- geek_cafe_saas_sdk/domains/payments/handlers/README.md +334 -0
- geek_cafe_saas_sdk/domains/payments/handlers/__init__.py +6 -0
- geek_cafe_saas_sdk/domains/payments/handlers/billing_accounts/create/app.py +105 -0
- geek_cafe_saas_sdk/domains/payments/handlers/billing_accounts/get/app.py +60 -0
- geek_cafe_saas_sdk/domains/payments/handlers/billing_accounts/update/app.py +97 -0
- geek_cafe_saas_sdk/domains/payments/handlers/payment_intents/create/app.py +97 -0
- geek_cafe_saas_sdk/domains/payments/handlers/payment_intents/get/app.py +60 -0
- geek_cafe_saas_sdk/domains/payments/handlers/payments/get/app.py +60 -0
- geek_cafe_saas_sdk/domains/payments/handlers/payments/list/app.py +68 -0
- geek_cafe_saas_sdk/domains/payments/handlers/payments/record/app.py +118 -0
- geek_cafe_saas_sdk/domains/payments/handlers/refunds/create/app.py +89 -0
- geek_cafe_saas_sdk/domains/payments/handlers/refunds/get/app.py +60 -0
- geek_cafe_saas_sdk/domains/payments/models/__init__.py +17 -0
- geek_cafe_saas_sdk/domains/payments/models/billing_account.py +521 -0
- geek_cafe_saas_sdk/domains/payments/models/payment.py +639 -0
- geek_cafe_saas_sdk/domains/payments/models/payment_intent_ref.py +539 -0
- geek_cafe_saas_sdk/domains/payments/models/refund.py +404 -0
- geek_cafe_saas_sdk/domains/payments/services/__init__.py +11 -0
- geek_cafe_saas_sdk/domains/payments/services/payment_service.py +405 -0
- geek_cafe_saas_sdk/domains/subscriptions/__init__.py +19 -0
- geek_cafe_saas_sdk/domains/subscriptions/handlers/README.md +408 -0
- geek_cafe_saas_sdk/domains/subscriptions/handlers/__init__.py +1 -0
- geek_cafe_saas_sdk/domains/subscriptions/handlers/addons/create/app.py +81 -0
- geek_cafe_saas_sdk/domains/subscriptions/handlers/addons/get/app.py +48 -0
- geek_cafe_saas_sdk/domains/subscriptions/handlers/addons/list/app.py +54 -0
- geek_cafe_saas_sdk/domains/subscriptions/handlers/addons/update/app.py +54 -0
- geek_cafe_saas_sdk/domains/subscriptions/handlers/discounts/create/app.py +83 -0
- geek_cafe_saas_sdk/domains/subscriptions/handlers/discounts/get/app.py +47 -0
- geek_cafe_saas_sdk/domains/subscriptions/handlers/discounts/validate/app.py +62 -0
- geek_cafe_saas_sdk/domains/subscriptions/handlers/plans/create/app.py +82 -0
- geek_cafe_saas_sdk/domains/subscriptions/handlers/plans/get/app.py +48 -0
- geek_cafe_saas_sdk/domains/subscriptions/handlers/plans/list/app.py +66 -0
- geek_cafe_saas_sdk/domains/subscriptions/handlers/plans/update/app.py +54 -0
- geek_cafe_saas_sdk/domains/subscriptions/handlers/usage/aggregate/app.py +72 -0
- geek_cafe_saas_sdk/domains/subscriptions/handlers/usage/record/app.py +89 -0
- geek_cafe_saas_sdk/domains/subscriptions/models/__init__.py +13 -0
- geek_cafe_saas_sdk/domains/subscriptions/models/addon.py +604 -0
- geek_cafe_saas_sdk/domains/subscriptions/models/discount.py +492 -0
- geek_cafe_saas_sdk/domains/subscriptions/models/plan.py +569 -0
- geek_cafe_saas_sdk/domains/subscriptions/models/usage_record.py +300 -0
- geek_cafe_saas_sdk/domains/subscriptions/services/__init__.py +10 -0
- geek_cafe_saas_sdk/domains/subscriptions/services/subscription_manager_service.py +694 -0
- geek_cafe_saas_sdk/domains/tenancy/models/subscription.py +123 -1
- geek_cafe_saas_sdk/domains/tenancy/services/subscription_service.py +213 -0
- geek_cafe_saas_sdk/lambda_handlers/_base/base_handler.py +7 -0
- geek_cafe_saas_sdk/services/database_service.py +10 -6
- geek_cafe_saas_sdk/utilities/environment_variables.py +16 -0
- geek_cafe_saas_sdk/utilities/logging_utility.py +77 -0
- {geek_cafe_saas_sdk-0.7.0.dist-info → geek_cafe_saas_sdk-0.7.2.dist-info}/METADATA +1 -1
- {geek_cafe_saas_sdk-0.7.0.dist-info → geek_cafe_saas_sdk-0.7.2.dist-info}/RECORD +79 -20
- {geek_cafe_saas_sdk-0.7.0.dist-info → geek_cafe_saas_sdk-0.7.2.dist-info}/WHEEL +0 -0
- {geek_cafe_saas_sdk-0.7.0.dist-info → geek_cafe_saas_sdk-0.7.2.dist-info}/licenses/LICENSE +0 -0
|
@@ -109,27 +109,8 @@ class FileShareService(DatabaseService[FileShare]):
|
|
|
109
109
|
share.access_count = 0
|
|
110
110
|
|
|
111
111
|
# Save to DynamoDB
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
item = share.to_dictionary()
|
|
116
|
-
item["pk"] = pk
|
|
117
|
-
item["sk"] = sk
|
|
118
|
-
|
|
119
|
-
# GSI1: Shares by file
|
|
120
|
-
item["gsi1_pk"] = f"FILE#{tenant_id}#{file_id}"
|
|
121
|
-
item["gsi1_sk"] = f"USER#{shared_with_user_id}"
|
|
122
|
-
|
|
123
|
-
# GSI2: Shares with user
|
|
124
|
-
item["gsi2_pk"] = f"TENANT#{tenant_id}#USER#{shared_with_user_id}"
|
|
125
|
-
item["gsi2_sk"] = f"FILE#{file_id}#{share.created_utc_ts}"
|
|
126
|
-
|
|
127
|
-
self.dynamodb.save(
|
|
128
|
-
item=item,
|
|
129
|
-
table_name=self.table_name
|
|
130
|
-
)
|
|
131
|
-
|
|
132
|
-
return ServiceResult.success_result(share)
|
|
112
|
+
share.prep_for_save()
|
|
113
|
+
return self._save_model(share)
|
|
133
114
|
|
|
134
115
|
except (ValidationError, AccessDeniedError) as e:
|
|
135
116
|
return ServiceResult.error_result(
|
|
@@ -163,23 +144,12 @@ class FileShareService(DatabaseService[FileShare]):
|
|
|
163
144
|
ServiceResult with FileShare model
|
|
164
145
|
"""
|
|
165
146
|
try:
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
pk = f"FILE#{tenant_id}#{file_id}"
|
|
170
|
-
sk = f"SHARE#{resource_id}"
|
|
171
|
-
|
|
172
|
-
result = self.dynamodb.get(
|
|
173
|
-
table_name=self.table_name,
|
|
174
|
-
key={"pk": pk, "sk": sk}
|
|
175
|
-
)
|
|
147
|
+
# Use helper method with tenant check
|
|
148
|
+
share = self._get_model_by_id_with_tenant_check(resource_id, FileShare, tenant_id)
|
|
176
149
|
|
|
177
|
-
if not
|
|
150
|
+
if not share:
|
|
178
151
|
raise NotFoundError(f"Share not found: {resource_id}")
|
|
179
152
|
|
|
180
|
-
share = FileShare()
|
|
181
|
-
share.map(result['Item'])
|
|
182
|
-
|
|
183
153
|
# Access control: user must be sharer or sharee
|
|
184
154
|
if share.shared_by != user_id and share.shared_with_user_id != user_id:
|
|
185
155
|
raise AccessDeniedError("You do not have access to this share")
|
|
@@ -256,25 +226,8 @@ class FileShareService(DatabaseService[FileShare]):
|
|
|
256
226
|
share.updated_utc_ts = dt.datetime.now(dt.UTC).timestamp()
|
|
257
227
|
|
|
258
228
|
# Save to DynamoDB
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
item = share.to_dictionary()
|
|
263
|
-
item["pk"] = pk
|
|
264
|
-
item["sk"] = sk
|
|
265
|
-
|
|
266
|
-
# Preserve GSI keys
|
|
267
|
-
item["gsi1_pk"] = f"FILE#{tenant_id}#{file_id}"
|
|
268
|
-
item["gsi1_sk"] = f"USER#{share.shared_with_user_id}"
|
|
269
|
-
item["gsi2_pk"] = f"TENANT#{tenant_id}#USER#{share.shared_with_user_id}"
|
|
270
|
-
item["gsi2_sk"] = f"FILE#{file_id}#{share.created_utc_ts}"
|
|
271
|
-
|
|
272
|
-
self.dynamodb.save(
|
|
273
|
-
item=item,
|
|
274
|
-
table_name=self.table_name
|
|
275
|
-
)
|
|
276
|
-
|
|
277
|
-
return ServiceResult.success_result(share)
|
|
229
|
+
share.prep_for_save()
|
|
230
|
+
return self._save_model(share)
|
|
278
231
|
|
|
279
232
|
except (ValidationError, AccessDeniedError) as e:
|
|
280
233
|
return ServiceResult.error_result(
|
|
@@ -323,23 +276,11 @@ class FileShareService(DatabaseService[FileShare]):
|
|
|
323
276
|
share.status = "revoked"
|
|
324
277
|
share.revoked_at = dt.datetime.now(dt.UTC).timestamp()
|
|
325
278
|
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
item = share.to_dictionary()
|
|
330
|
-
item["pk"] = pk
|
|
331
|
-
item["sk"] = sk
|
|
279
|
+
share.prep_for_save()
|
|
280
|
+
save_result = self._save_model(share)
|
|
332
281
|
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
item["gsi1_sk"] = f"USER#{share.shared_with_user_id}"
|
|
336
|
-
item["gsi2_pk"] = f"TENANT#{tenant_id}#USER#{share.shared_with_user_id}"
|
|
337
|
-
item["gsi2_sk"] = f"FILE#{file_id}#{share.created_utc_ts}"
|
|
338
|
-
|
|
339
|
-
self.dynamodb.save(
|
|
340
|
-
item=item,
|
|
341
|
-
table_name=self.table_name
|
|
342
|
-
)
|
|
282
|
+
if not save_result.success:
|
|
283
|
+
return save_result
|
|
343
284
|
|
|
344
285
|
return ServiceResult.success_result(True)
|
|
345
286
|
|
|
@@ -383,21 +324,19 @@ class FileShareService(DatabaseService[FileShare]):
|
|
|
383
324
|
error_code=ErrorCode.ACCESS_DENIED
|
|
384
325
|
)
|
|
385
326
|
|
|
386
|
-
#
|
|
387
|
-
|
|
327
|
+
# Use GSI1 to query shares by file
|
|
328
|
+
temp_share = FileShare()
|
|
329
|
+
temp_share.file_id = file_id
|
|
388
330
|
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
table_name=self.table_name,
|
|
392
|
-
index_name="gsi1",
|
|
393
|
-
limit=limit
|
|
394
|
-
)
|
|
331
|
+
# Query using helper method
|
|
332
|
+
query_result = self._query_by_index(temp_share, "gsi1", limit=limit, ascending=False)
|
|
395
333
|
|
|
334
|
+
if not query_result.success:
|
|
335
|
+
return query_result
|
|
336
|
+
|
|
337
|
+
# Filter results
|
|
396
338
|
shares = []
|
|
397
|
-
for
|
|
398
|
-
share = FileShare()
|
|
399
|
-
share.map(item)
|
|
400
|
-
|
|
339
|
+
for share in query_result.data:
|
|
401
340
|
# Include active and expired shares, exclude revoked
|
|
402
341
|
if share.status != "revoked":
|
|
403
342
|
shares.append(share)
|
|
@@ -429,21 +368,19 @@ class FileShareService(DatabaseService[FileShare]):
|
|
|
429
368
|
ServiceResult with list of FileShare models
|
|
430
369
|
"""
|
|
431
370
|
try:
|
|
432
|
-
#
|
|
433
|
-
|
|
371
|
+
# Use GSI2 to query shares by shared_with_user
|
|
372
|
+
temp_share = FileShare()
|
|
373
|
+
temp_share.shared_with_user_id = user_id
|
|
434
374
|
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
)
|
|
375
|
+
# Query using helper method
|
|
376
|
+
query_result = self._query_by_index(temp_share, "gsi2", limit=limit, ascending=False)
|
|
377
|
+
|
|
378
|
+
if not query_result.success:
|
|
379
|
+
return query_result
|
|
441
380
|
|
|
381
|
+
# Filter results
|
|
442
382
|
shares = []
|
|
443
|
-
for
|
|
444
|
-
share = FileShare()
|
|
445
|
-
share.map(item)
|
|
446
|
-
|
|
383
|
+
for share in query_result.data:
|
|
447
384
|
# Only include active, non-expired shares
|
|
448
385
|
if share.is_active:
|
|
449
386
|
shares.append(share)
|
|
@@ -543,20 +480,12 @@ class FileShareService(DatabaseService[FileShare]):
|
|
|
543
480
|
def _get_file(self, tenant_id: str, file_id: str, user_id: str) -> ServiceResult[File]:
|
|
544
481
|
"""Get file with access control."""
|
|
545
482
|
try:
|
|
546
|
-
|
|
547
|
-
|
|
483
|
+
# Use helper method with tenant check
|
|
484
|
+
file = self._get_model_by_id_with_tenant_check(file_id, File, tenant_id)
|
|
548
485
|
|
|
549
|
-
|
|
550
|
-
table_name=self.table_name,
|
|
551
|
-
key={"pk": pk, "sk": sk}
|
|
552
|
-
)
|
|
553
|
-
|
|
554
|
-
if not result or 'Item' not in result:
|
|
486
|
+
if not file:
|
|
555
487
|
raise NotFoundError(f"File not found: {file_id}")
|
|
556
488
|
|
|
557
|
-
file = File()
|
|
558
|
-
file.map(result['Item'])
|
|
559
|
-
|
|
560
489
|
if file.owner_id != user_id:
|
|
561
490
|
raise AccessDeniedError("You do not have access to this file")
|
|
562
491
|
|
|
@@ -571,12 +500,14 @@ class FileShareService(DatabaseService[FileShare]):
|
|
|
571
500
|
def _get_file_any_user(self, tenant_id: str, file_id: str) -> ServiceResult[File]:
|
|
572
501
|
"""Get file without access control (for internal use)."""
|
|
573
502
|
try:
|
|
574
|
-
|
|
575
|
-
|
|
503
|
+
|
|
504
|
+
file = File()
|
|
505
|
+
file.id = file_id
|
|
506
|
+
file.tenant_id = tenant_id
|
|
576
507
|
|
|
577
508
|
result = self.dynamodb.get(
|
|
578
509
|
table_name=self.table_name,
|
|
579
|
-
|
|
510
|
+
model=file
|
|
580
511
|
)
|
|
581
512
|
|
|
582
513
|
if not result or 'Item' not in result:
|
|
@@ -601,21 +532,21 @@ class FileShareService(DatabaseService[FileShare]):
|
|
|
601
532
|
) -> Optional[FileShare]:
|
|
602
533
|
"""Check if share already exists."""
|
|
603
534
|
try:
|
|
604
|
-
|
|
605
|
-
|
|
535
|
+
# Query GSI1 by file_id to get all shares for this file
|
|
536
|
+
temp_share = FileShare()
|
|
537
|
+
temp_share.file_id = file_id
|
|
606
538
|
|
|
607
|
-
|
|
608
|
-
key=Key('gsi1_pk').eq(gsi1_pk) & Key('gsi1_sk').eq(gsi1_sk),
|
|
609
|
-
table_name=self.table_name,
|
|
610
|
-
index_name="gsi1",
|
|
611
|
-
limit=1
|
|
612
|
-
)
|
|
539
|
+
result = self._query_by_index(temp_share, "gsi1", limit=100)
|
|
613
540
|
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
541
|
+
if not result.success:
|
|
542
|
+
return None
|
|
543
|
+
|
|
544
|
+
# Filter for matching tenant and user (active shares only)
|
|
545
|
+
for share in result.data:
|
|
546
|
+
if (share.tenant_id == tenant_id and
|
|
547
|
+
share.shared_with_user_id == shared_with_user_id and
|
|
548
|
+
share.status == "active"):
|
|
549
|
+
return share
|
|
619
550
|
|
|
620
551
|
return None
|
|
621
552
|
|
|
@@ -630,12 +561,14 @@ class FileShareService(DatabaseService[FileShare]):
|
|
|
630
561
|
) -> None:
|
|
631
562
|
"""Increment share access count."""
|
|
632
563
|
try:
|
|
633
|
-
|
|
634
|
-
|
|
564
|
+
share = FileShare()
|
|
565
|
+
share.id = share_id
|
|
566
|
+
share.tenant_id = tenant_id
|
|
567
|
+
share.file_id = file_id
|
|
635
568
|
|
|
636
569
|
result = self.dynamodb.get(
|
|
637
570
|
table_name=self.table_name,
|
|
638
|
-
|
|
571
|
+
model=share
|
|
639
572
|
)
|
|
640
573
|
|
|
641
574
|
if result and 'Item' in result:
|
|
@@ -645,19 +578,10 @@ class FileShareService(DatabaseService[FileShare]):
|
|
|
645
578
|
share.access_count += 1
|
|
646
579
|
share.last_accessed_at = dt.datetime.now(dt.UTC).timestamp()
|
|
647
580
|
|
|
648
|
-
|
|
649
|
-
item["pk"] = pk
|
|
650
|
-
item["sk"] = sk
|
|
651
|
-
|
|
652
|
-
# Preserve GSI keys
|
|
653
|
-
item["gsi1_pk"] = result['Item'].get('gsi1_pk')
|
|
654
|
-
item["gsi1_sk"] = result['Item'].get('gsi1_sk')
|
|
655
|
-
item["gsi2_pk"] = result['Item'].get('gsi2_pk')
|
|
656
|
-
item["gsi2_sk"] = result['Item'].get('gsi2_sk')
|
|
657
|
-
|
|
581
|
+
share.prep_for_save()
|
|
658
582
|
self.dynamodb.save(
|
|
659
|
-
|
|
660
|
-
|
|
583
|
+
table_name=self.table_name,
|
|
584
|
+
item=share
|
|
661
585
|
)
|
|
662
586
|
except Exception:
|
|
663
587
|
pass # Best effort
|
|
@@ -174,28 +174,11 @@ class FileSystemService(DatabaseService[File]):
|
|
|
174
174
|
)
|
|
175
175
|
|
|
176
176
|
# Save metadata to DynamoDB
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
item = file.to_dictionary()
|
|
181
|
-
item["pk"] = pk
|
|
182
|
-
item["sk"] = sk
|
|
183
|
-
|
|
184
|
-
# GSI1: Files by directory
|
|
185
|
-
item["gsi1_pk"] = f"TENANT#{tenant_id}"
|
|
186
|
-
if directory_id:
|
|
187
|
-
item["gsi1_sk"] = f"DIRECTORY#{directory_id}#{file_name}"
|
|
188
|
-
else:
|
|
189
|
-
item["gsi1_sk"] = f"DIRECTORY#ROOT#{file_name}"
|
|
190
|
-
|
|
191
|
-
# GSI2: Files by owner
|
|
192
|
-
item["gsi2_pk"] = f"TENANT#{tenant_id}#USER#{user_id}"
|
|
193
|
-
item["gsi2_sk"] = f"FILE#{file.created_utc_ts}"
|
|
177
|
+
file.prep_for_save()
|
|
178
|
+
save_result = self._save_model(file)
|
|
194
179
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
item=item
|
|
198
|
-
)
|
|
180
|
+
if not save_result.success:
|
|
181
|
+
return save_result
|
|
199
182
|
|
|
200
183
|
return ServiceResult.success_result(file)
|
|
201
184
|
|
|
@@ -229,23 +212,13 @@ class FileSystemService(DatabaseService[File]):
|
|
|
229
212
|
ServiceResult with File model
|
|
230
213
|
"""
|
|
231
214
|
try:
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
result = self.dynamodb.get(
|
|
236
|
-
table_name=self.table_name,
|
|
237
|
-
key={"pk": pk, "sk": sk}
|
|
238
|
-
)
|
|
215
|
+
# Use helper method with tenant check
|
|
216
|
+
file = self._get_model_by_id_with_tenant_check(resource_id, File, tenant_id)
|
|
239
217
|
|
|
240
|
-
# Check if file exists
|
|
241
|
-
|
|
242
|
-
if not result or 'Item' not in result:
|
|
218
|
+
# Check if file exists
|
|
219
|
+
if not file:
|
|
243
220
|
raise NotFoundError(f"File not found: {resource_id}")
|
|
244
221
|
|
|
245
|
-
# Convert to File model
|
|
246
|
-
file = File()
|
|
247
|
-
file.map(result['Item'])
|
|
248
|
-
|
|
249
222
|
# Access control: Check if user is owner or has share access
|
|
250
223
|
if file.owner_id != user_id:
|
|
251
224
|
# TODO: Check FileShare for access
|
|
@@ -316,27 +289,8 @@ class FileSystemService(DatabaseService[File]):
|
|
|
316
289
|
file.updated_utc_ts = dt.datetime.now(dt.UTC).timestamp()
|
|
317
290
|
|
|
318
291
|
# Save to DynamoDB
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
item = file.to_dictionary()
|
|
323
|
-
item["pk"] = pk
|
|
324
|
-
item["sk"] = sk
|
|
325
|
-
|
|
326
|
-
# Update GSI keys if directory changed
|
|
327
|
-
if "directory_id" in updates:
|
|
328
|
-
item["gsi1_pk"] = f"TENANT#{tenant_id}"
|
|
329
|
-
if updates["directory_id"]:
|
|
330
|
-
item["gsi1_sk"] = f"DIRECTORY#{updates['directory_id']}#{file.file_name}"
|
|
331
|
-
else:
|
|
332
|
-
item["gsi1_sk"] = f"DIRECTORY#ROOT#{file.file_name}"
|
|
333
|
-
|
|
334
|
-
self.dynamodb.save(
|
|
335
|
-
table_name=self.table_name,
|
|
336
|
-
item=item
|
|
337
|
-
)
|
|
338
|
-
|
|
339
|
-
return ServiceResult.success_result(file)
|
|
292
|
+
file.prep_for_save()
|
|
293
|
+
return self._save_model(file)
|
|
340
294
|
|
|
341
295
|
except AccessDeniedError as e:
|
|
342
296
|
return ServiceResult.error_result(
|
|
@@ -396,11 +350,12 @@ class FileSystemService(DatabaseService[File]):
|
|
|
396
350
|
)
|
|
397
351
|
|
|
398
352
|
# Delete from DynamoDB
|
|
399
|
-
|
|
400
|
-
|
|
353
|
+
file = File()
|
|
354
|
+
file.id = resource_id
|
|
355
|
+
file.tenant_id = tenant_id
|
|
401
356
|
|
|
402
357
|
self.dynamodb.delete(
|
|
403
|
-
|
|
358
|
+
model=file,
|
|
404
359
|
table_name=self.table_name
|
|
405
360
|
)
|
|
406
361
|
else:
|
|
@@ -409,17 +364,11 @@ class FileSystemService(DatabaseService[File]):
|
|
|
409
364
|
file.status = "deleted"
|
|
410
365
|
file.deleted_utc_ts = dt.datetime.now(dt.UTC).timestamp()
|
|
411
366
|
|
|
412
|
-
|
|
413
|
-
|
|
367
|
+
file.prep_for_save()
|
|
368
|
+
save_result = self._save_model(file)
|
|
414
369
|
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
item["sk"] = sk
|
|
418
|
-
|
|
419
|
-
self.dynamodb.save(
|
|
420
|
-
table_name=self.table_name,
|
|
421
|
-
item=item
|
|
422
|
-
)
|
|
370
|
+
if not save_result.success:
|
|
371
|
+
return save_result
|
|
423
372
|
|
|
424
373
|
return ServiceResult.success_result(True)
|
|
425
374
|
|
|
@@ -507,32 +456,23 @@ class FileSystemService(DatabaseService[File]):
|
|
|
507
456
|
ServiceResult with list of File models
|
|
508
457
|
"""
|
|
509
458
|
try:
|
|
510
|
-
|
|
459
|
+
# Use GSI1 to query files by directory
|
|
460
|
+
temp_file = File()
|
|
461
|
+
temp_file.tenant_id = tenant_id
|
|
462
|
+
temp_file.directory_id = directory_id # None for root files
|
|
511
463
|
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
# Query GSI1
|
|
518
|
-
results = self.dynamodb.query(
|
|
519
|
-
key=Key('gsi1_pk').eq(gsi1_pk) & Key('gsi1_sk').begins_with(gsi1_sk_prefix),
|
|
520
|
-
table_name=self.table_name,
|
|
521
|
-
index_name="gsi1",
|
|
522
|
-
limit=limit
|
|
523
|
-
)
|
|
464
|
+
# Query using helper method
|
|
465
|
+
query_result = self._query_by_index(temp_file, "gsi1", limit=limit, ascending=True)
|
|
466
|
+
|
|
467
|
+
if not query_result.success:
|
|
468
|
+
return query_result
|
|
524
469
|
|
|
470
|
+
# Filter results
|
|
525
471
|
files = []
|
|
526
|
-
for
|
|
527
|
-
|
|
528
|
-
file.
|
|
529
|
-
|
|
530
|
-
# Filter out deleted files
|
|
531
|
-
if file.status != "deleted":
|
|
532
|
-
# Basic access control: show only owned files or shared files
|
|
533
|
-
# TODO: Check FileShare for shared access
|
|
534
|
-
if file.owner_id == user_id:
|
|
535
|
-
files.append(file)
|
|
472
|
+
for file in query_result.data:
|
|
473
|
+
# Filter out deleted files and apply access control
|
|
474
|
+
if file.status != "deleted" and file.owner_id == user_id:
|
|
475
|
+
files.append(file)
|
|
536
476
|
|
|
537
477
|
return ServiceResult.success_result(files)
|
|
538
478
|
|
|
@@ -567,21 +507,20 @@ class FileSystemService(DatabaseService[File]):
|
|
|
567
507
|
if owner_id != user_id:
|
|
568
508
|
raise AccessDeniedError("You can only list your own files")
|
|
569
509
|
|
|
570
|
-
|
|
510
|
+
# Use GSI2 to query files by owner
|
|
511
|
+
temp_file = File()
|
|
512
|
+
temp_file.tenant_id = tenant_id
|
|
513
|
+
temp_file.owner_id = owner_id
|
|
571
514
|
|
|
572
|
-
# Query
|
|
573
|
-
|
|
574
|
-
key=Key('gsi2_pk').eq(gsi2_pk) & Key('gsi2_sk').begins_with("FILE#"),
|
|
575
|
-
table_name=self.table_name,
|
|
576
|
-
index_name="gsi2",
|
|
577
|
-
limit=limit
|
|
578
|
-
)
|
|
515
|
+
# Query using helper method
|
|
516
|
+
query_result = self._query_by_index(temp_file, "gsi2", limit=limit, ascending=False)
|
|
579
517
|
|
|
518
|
+
if not query_result.success:
|
|
519
|
+
return query_result
|
|
520
|
+
|
|
521
|
+
# Filter results
|
|
580
522
|
files = []
|
|
581
|
-
for
|
|
582
|
-
file = File()
|
|
583
|
-
file.map(item)
|
|
584
|
-
|
|
523
|
+
for file in query_result.data:
|
|
585
524
|
# Filter out deleted files
|
|
586
525
|
if file.status != "deleted":
|
|
587
526
|
files.append(file)
|