seaislee1209 85f76d8543 feat: v0.8.2~v0.8.4 — 管理后台 UI 修复 + 团队详情重构 + 审计日志系统
v0.8.2: DatePicker/Select 暗色主题、公告跑马灯、Toast 全局化、失败原因 tooltip
v0.8.3: 团队详情抽屉→弹窗重构 + 修改秒数池功能 + member_count 修复
v0.8.4: AdminAuditLog 模型 + 12 处管理操作埋点 + 日志查询页面(/admin/logs)

审计日志覆盖所有管理员 mutation 操作(充值、修改额度、创建/禁用用户等),
记录操作人、变更前后值、IP 地址,支持按操作类型/操作人/日期筛选。

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-16 01:18:44 +08:00

93 lines
2.6 KiB
Python

"""Volcano Engine TOS file upload utility using official TOS SDK."""
import hashlib
import uuid
import logging
from django.conf import settings
logger = logging.getLogger(__name__)
CONTENT_TYPE_MAP = {
'jpg': 'image/jpeg', 'jpeg': 'image/jpeg', 'png': 'image/png',
'webp': 'image/webp', 'gif': 'image/gif', 'bmp': 'image/bmp',
'tiff': 'image/tiff',
'mp4': 'video/mp4', 'mov': 'video/quicktime',
'mp3': 'audio/mpeg', 'wav': 'audio/wav',
}
_client = None
def get_tos_client():
import tos
global _client
if _client is None:
endpoint = settings.TOS_ENDPOINT.replace('https://', '').replace('http://', '')
_client = tos.TosClientV2(
ak=settings.TOS_ACCESS_KEY,
sk=settings.TOS_SECRET_KEY,
endpoint=endpoint,
region=settings.TOS_REGION,
)
return _client
def upload_file(file_obj, folder='uploads'):
"""Upload a file to TOS bucket with content-hash dedup, return its public URL.
Uses MD5 hash of file content as the object key. If the same file
has already been uploaded, the existing URL is returned without
re-uploading, saving storage and bandwidth.
"""
ext = file_obj.name.rsplit('.', 1)[-1].lower()
content_type = CONTENT_TYPE_MAP.get(ext, 'application/octet-stream')
client = get_tos_client()
content = file_obj.read()
# Use content hash as key for dedup
content_hash = hashlib.md5(content).hexdigest()
key = f'{folder}/{content_hash}.{ext}'
url = f'{settings.TOS_CDN_DOMAIN}/{key}'
# Check if object already exists — skip upload if so
try:
client.head_object(bucket=settings.TOS_BUCKET, key=key)
logger.info('TOS dedup hit: %s', key)
return url
except Exception:
pass # Object doesn't exist, proceed with upload
client.put_object(
bucket=settings.TOS_BUCKET,
key=key,
content=content,
content_type=content_type,
)
return url
def upload_from_url(source_url, folder='results'):
"""Download a file from a URL and upload to TOS, return permanent CDN URL."""
import requests as req
resp = req.get(source_url, timeout=120, stream=True)
resp.raise_for_status()
content = resp.content
content_type = resp.headers.get('Content-Type', 'video/mp4')
ext = 'mp4' # Seedance always returns mp4
key = f'{folder}/{uuid.uuid4().hex}.{ext}'
client = get_tos_client()
client.put_object(
bucket=settings.TOS_BUCKET,
key=key,
content=content,
content_type=content_type,
)
return f'{settings.TOS_CDN_DOMAIN}/{key}'