feat: 增加操作审计与受控下载

This commit is contained in:
shenlei
2026-07-22 18:12:19 +09:00
parent 624f534b0f
commit 4dccc4d96b
15 changed files with 323 additions and 9 deletions
+23
View File
@@ -0,0 +1,23 @@
"""操作审计日志。"""
import json
import uuid
from datetime import datetime
from ..models import OperationLog
def record_operation(db, user: dict, action: str, *, request=None, task=None,
resource_type: str = "", resource_name: str = "", detail: dict | None = None):
"""将已登录用户的关键操作写入当前数据库事务。"""
client_ip = request.client.host if request and request.client else ""
user_agent = request.headers.get("user-agent", "")[:500] if request else ""
db.add(OperationLog(
id=str(uuid.uuid4()), created_at=datetime.utcnow(), username=user.get("username", ""),
is_admin=bool(user.get("is_admin")), action=action,
task_id=task.id if task else None,
app_id=task.app_id if task else None,
app_name=task.app_name if task else None,
resource_type=resource_type, resource_name=resource_name,
detail_json=json.dumps(detail or {}, ensure_ascii=False),
client_ip=client_ip, user_agent=user_agent,
))
+26
View File
@@ -9,6 +9,8 @@ from urllib.parse import quote, unquote, urlparse
import httpx
from ..config import DOWNLOAD_URL_EXPIRE_SECONDS
class DistributionError(Exception):
"""分发配置或上传过程失败。"""
@@ -167,6 +169,30 @@ def delete_published_artifacts(
deleter(upload_config, remote_paths)
def get_published_download_url(build_config: dict, upload_config: dict, published_url: str) -> str:
"""返回用于受控下载的短时链接;WebDAV 保持原公开链接。"""
if upload_config.get("mode") != "oss":
return published_url
remote_paths = _remote_paths_from_published_url(build_config, upload_config, published_url)
if not remote_paths:
raise DistributionError("无法识别 OSS 文件路径,无法生成受控下载链接")
oss = upload_config.get("oss", {})
required = ["access_key_id", "access_key_secret", "endpoint", "bucket_name"]
if any(not oss.get(key) for key in required):
raise DistributionError("OSS 配置不完整")
try:
import oss2
except ImportError as exc:
raise DistributionError("未安装 oss2,请重新执行 ./deploy.sh build") from exc
bucket = oss2.Bucket(
oss2.Auth(oss["access_key_id"], oss["access_key_secret"]),
oss["endpoint"], oss["bucket_name"], connect_timeout=30,
)
return bucket.sign_url("GET", remote_paths[0], DOWNLOAD_URL_EXPIRE_SECONDS, slash_safe=True)
def _ensure_webdav_dirs(client: httpx.Client, server_url: str, remote_path: str):
path = ""
for segment in remote_path.strip("/").split("/")[:-1]: