v1.9.2: S3 right-click context menu — copy presigned download link

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
chrome-storm-c442
2026-03-03 07:36:21 -05:00
parent 1e729fcf3a
commit e403da4f9d
5 changed files with 98 additions and 1 deletions

View File

@@ -390,6 +390,10 @@ _EN = {
"s3_uploading_n": "Uploading {count} files...",
"s3_uploaded_n": "Uploaded {count} files",
"s3_upload_partial": "Uploaded {ok}/{total} files",
"s3_copy_link": "Copy Link",
"s3_generating_link": "Generating link...",
"s3_link_copied": "Link copied to clipboard",
"s3_link_failed": "Failed to generate link",
# Grafana tab
"grafana_refresh": "Refresh",
@@ -892,6 +896,10 @@ _RU = {
"s3_uploading_n": "Загрузка {count} файлов...",
"s3_uploaded_n": "Загружено {count} файлов",
"s3_upload_partial": "Загружено {ok}/{total} файлов",
"s3_copy_link": "Копировать ссылку",
"s3_generating_link": "Генерация ссылки...",
"s3_link_copied": "Ссылка скопирована",
"s3_link_failed": "Ошибка генерации ссылки",
# Grafana tab
"grafana_refresh": "Обновить",
@@ -1394,6 +1402,10 @@ _ZH = {
"s3_uploading_n": "正在上传 {count} 个文件...",
"s3_uploaded_n": "已上传 {count} 个文件",
"s3_upload_partial": "已上传 {ok}/{total} 个文件",
"s3_copy_link": "复制链接",
"s3_generating_link": "生成链接中...",
"s3_link_copied": "链接已复制",
"s3_link_failed": "生成链接失败",
# Grafana tab
"grafana_refresh": "刷新",

View File

@@ -277,6 +277,26 @@ class S3Client:
log.error("S3 delete failed: %s", exc)
return False
def generate_presigned_url(self, bucket: str, key: str,
expires_in: int = 3600) -> str | None:
"""Generate a presigned download URL for an object.
expires_in: URL lifetime in seconds (default 1 hour).
Returns URL string or None on failure.
"""
if not self._ensure_connected():
return None
try:
url = self._client.generate_presigned_url(
"get_object",
Params={"Bucket": bucket, "Key": key},
ExpiresIn=expires_in,
)
return url
except Exception as exc:
log.error("S3 presigned URL failed: %s", exc)
return None
def get_object_size(self, bucket: str, key: str) -> int:
"""Get size of an object in bytes."""
if not self._ensure_connected():