feat: add S3 storage support, policy migration, and quota enforcement
Some checks failed
Test / test (push) Failing after 2m21s
Some checks failed
Test / test (push) Failing after 2m21s
- Add S3StorageService with AWS Signature V4 signing (URI-encoded for non-ASCII keys)
- Add PATCH /object/{id}/policy endpoint for switching storage policies with background migration
- Implement cross-storage file migration service (local <-> S3)
- Replace deprecated StorageType enum with PolicyType (local/s3)
- Implement GET /user/settings/policies endpoint (was 501 stub)
- Add storage quota pre-allocation on upload session creation to prevent concurrent bypass
- Fix BigInteger for max_storage and user.storage to support >2GB values
- Add policy permission validation on upload and directory creation
- Use group's first policy as default on registration instead of hardcoded name
- Define TaskType.POLICY_MIGRATE and extend TaskProps with migration fields
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -6,7 +6,8 @@ from sqlalchemy import update as sql_update
|
||||
from sqlalchemy.sql.functions import func
|
||||
from middleware.dependencies import SessionDep
|
||||
|
||||
from service.storage import LocalStorageService
|
||||
from .local_storage import LocalStorageService
|
||||
from .s3_storage import S3StorageService
|
||||
from sqlmodels import (
|
||||
Object,
|
||||
PhysicalFile,
|
||||
@@ -271,10 +272,14 @@ async def permanently_delete_objects(
|
||||
if physical_file.can_be_deleted:
|
||||
# 物理删除文件
|
||||
policy = await Policy.get(session, Policy.id == physical_file.policy_id)
|
||||
if policy and policy.type == PolicyType.LOCAL:
|
||||
if policy:
|
||||
try:
|
||||
storage_service = LocalStorageService(policy)
|
||||
await storage_service.delete_file(physical_file.storage_path)
|
||||
if policy.type == PolicyType.LOCAL:
|
||||
storage_service = LocalStorageService(policy)
|
||||
await storage_service.delete_file(physical_file.storage_path)
|
||||
elif policy.type == PolicyType.S3:
|
||||
s3_service = await S3StorageService.from_policy(policy)
|
||||
await s3_service.delete_file(physical_file.storage_path)
|
||||
l.debug(f"物理文件已删除: {obj_name}")
|
||||
except Exception as e:
|
||||
l.warning(f"物理删除文件失败: {obj_name}, 错误: {e}")
|
||||
@@ -374,10 +379,19 @@ async def delete_object_recursive(
|
||||
if physical_file.can_be_deleted:
|
||||
# 物理删除文件
|
||||
policy = await Policy.get(session, Policy.id == physical_file.policy_id)
|
||||
if policy and policy.type == PolicyType.LOCAL:
|
||||
if policy:
|
||||
try:
|
||||
storage_service = LocalStorageService(policy)
|
||||
await storage_service.delete_file(physical_file.storage_path)
|
||||
if policy.type == PolicyType.LOCAL:
|
||||
storage_service = LocalStorageService(policy)
|
||||
await storage_service.delete_file(physical_file.storage_path)
|
||||
elif policy.type == PolicyType.S3:
|
||||
options = await policy.awaitable_attrs.options
|
||||
s3_service = S3StorageService(
|
||||
policy,
|
||||
region=options.s3_region if options else 'us-east-1',
|
||||
is_path_style=options.s3_path_style if options else False,
|
||||
)
|
||||
await s3_service.delete_file(physical_file.storage_path)
|
||||
l.debug(f"物理文件已删除: {obj_name}")
|
||||
except Exception as e:
|
||||
l.warning(f"物理删除文件失败: {obj_name}, 错误: {e}")
|
||||
|
||||
Reference in New Issue
Block a user