v1.9.5: S3 — new folder, folder delete with confirmation, folder drag-and-drop

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
chrome-storm-c442
2026-03-03 08:07:40 -05:00
parent 61461767fd
commit f233d5cf70
5 changed files with 130 additions and 17 deletions

View File

@@ -306,6 +306,43 @@ class S3Client:
log.error("S3 presigned URL failed: %s", exc)
return None
def delete_prefix(self, bucket: str, prefix: str) -> int:
"""Recursively delete all objects under a prefix. Returns count deleted."""
if not self._ensure_connected():
return 0
try:
deleted = 0
paginator = self._client.get_paginator("list_objects_v2")
for page in paginator.paginate(Bucket=bucket, Prefix=prefix):
objects = page.get("Contents", [])
if not objects:
continue
delete_req = {
"Objects": [{"Key": obj["Key"]} for obj in objects],
"Quiet": True,
}
self._client.delete_objects(Bucket=bucket, Delete=delete_req)
deleted += len(objects)
self._last_ok = time.time()
log.info("S3 deleted prefix s3://%s/%s (%d objects)", bucket, prefix, deleted)
return deleted
except Exception as exc:
log.error("S3 delete prefix failed: %s", exc)
return 0
def create_folder(self, bucket: str, key: str) -> bool:
"""Create a folder (empty object with trailing slash) in S3."""
if not self._ensure_connected():
return False
try:
self._client.put_object(Bucket=bucket, Key=key, Body=b"")
self._last_ok = time.time()
log.info("S3 created folder s3://%s/%s", bucket, key)
return True
except Exception as exc:
log.error("S3 create folder failed: %s", exc)
return False
def get_direct_url(self, bucket: str, key: str) -> str:
"""Build a direct (permanent) URL: endpoint/bucket/key."""
endpoint = self._endpoint.rstrip("/")