feat: 增加操作审计与受控下载
This commit is contained in:
@@ -6,7 +6,7 @@ import tempfile
|
||||
import uuid
|
||||
from datetime import datetime
|
||||
from typing import List
|
||||
from fastapi import APIRouter, Depends, HTTPException
|
||||
from fastapi import APIRouter, Depends, HTTPException, Request
|
||||
from fastapi.responses import FileResponse
|
||||
from starlette.background import BackgroundTask
|
||||
from sqlalchemy.orm import Session
|
||||
@@ -20,7 +20,7 @@ router = APIRouter(prefix="/api/tasks", tags=["tasks"], dependencies=[Depends(ge
|
||||
|
||||
|
||||
@router.post("", response_model=TaskResponse)
|
||||
async def create_task(task: TaskCreate, db: Session = Depends(get_db)):
|
||||
async def create_task(task: TaskCreate, request: Request, user: dict = Depends(get_current_user), db: Session = Depends(get_db)):
|
||||
"""创建打包任务"""
|
||||
from .config import load_config
|
||||
|
||||
@@ -63,6 +63,9 @@ async def create_task(task: TaskCreate, db: Session = Depends(get_db)):
|
||||
status="pending",
|
||||
)
|
||||
db.add(db_task)
|
||||
from ..services.audit_log import record_operation
|
||||
record_operation(db, user, "build_created", request=request, task=db_task,
|
||||
detail={"branch": task.branch, "build_type": task.build_type})
|
||||
db.commit()
|
||||
db.refresh(db_task)
|
||||
|
||||
@@ -122,7 +125,7 @@ async def get_task(task_id: str, db: Session = Depends(get_db)):
|
||||
|
||||
|
||||
@router.delete("/{task_id}")
|
||||
async def cancel_task(task_id: str, db: Session = Depends(get_db)):
|
||||
async def cancel_task(task_id: str, request: Request, user: dict = Depends(get_current_user), db: Session = Depends(get_db)):
|
||||
"""取消任务"""
|
||||
task = db.query(Task).filter(Task.id == task_id).first()
|
||||
if not task:
|
||||
@@ -132,6 +135,8 @@ async def cancel_task(task_id: str, db: Session = Depends(get_db)):
|
||||
raise HTTPException(status_code=400, detail="任务已完成或已取消")
|
||||
|
||||
task.status = "cancelled"
|
||||
from ..services.audit_log import record_operation
|
||||
record_operation(db, user, "build_cancelled", request=request, task=task)
|
||||
db.commit()
|
||||
|
||||
# 尝试取消运行中的任务
|
||||
@@ -153,7 +158,7 @@ async def cancel_task(task_id: str, db: Session = Depends(get_db)):
|
||||
|
||||
|
||||
@router.delete("/{task_id}/delete")
|
||||
async def delete_task(task_id: str, db: Session = Depends(get_db)):
|
||||
async def delete_task(task_id: str, request: Request, user: dict = Depends(get_current_user), db: Session = Depends(get_db)):
|
||||
"""删除打包记录"""
|
||||
import shutil
|
||||
from pathlib import Path
|
||||
@@ -199,6 +204,8 @@ async def delete_task(task_id: str, db: Session = Depends(get_db)):
|
||||
if build_path.exists():
|
||||
shutil.rmtree(build_path, ignore_errors=True)
|
||||
|
||||
from ..services.audit_log import record_operation
|
||||
record_operation(db, user, "task_deleted", request=request, task=task)
|
||||
db.delete(task)
|
||||
db.commit()
|
||||
|
||||
@@ -243,8 +250,35 @@ async def get_build_log(task_id: str, db: Session = Depends(get_db)):
|
||||
return {"log": ""}
|
||||
|
||||
|
||||
@router.get("/{task_id}/download-link")
|
||||
async def get_download_link(task_id: str, request: Request, user: dict = Depends(get_current_user), db: Session = Depends(get_db)):
|
||||
"""记录已登录用户发起 IPA 下载,并返回 OSS 公开链接。"""
|
||||
task = db.query(Task).filter(Task.id == task_id).first()
|
||||
if not task:
|
||||
raise HTTPException(status_code=404, detail="任务不存在")
|
||||
if not task.oss_url:
|
||||
raise HTTPException(status_code=404, detail="下载文件不存在")
|
||||
try:
|
||||
from .config import load_config
|
||||
from ..config import DOWNLOAD_URL_EXPIRE_SECONDS
|
||||
from ..services.distribution import DistributionError, get_published_download_url
|
||||
download_url = get_published_download_url(
|
||||
json.loads(task.config_json or "{}"),
|
||||
load_config().get("upload", {}),
|
||||
task.oss_url,
|
||||
)
|
||||
except (json.JSONDecodeError, DistributionError) as exc:
|
||||
raise HTTPException(status_code=502, detail=f"生成下载链接失败:{exc}") from exc
|
||||
from ..services.audit_log import record_operation
|
||||
record_operation(db, user, "ipa_download_requested", request=request, task=task,
|
||||
resource_type="ipa", resource_name=task.oss_url,
|
||||
detail={"controlled": True})
|
||||
db.commit()
|
||||
return {"url": download_url, "expires_in": DOWNLOAD_URL_EXPIRE_SECONDS}
|
||||
|
||||
|
||||
@router.get("/{task_id}/dsym")
|
||||
async def download_dsym(task_id: str, db: Session = Depends(get_db)):
|
||||
async def download_dsym(task_id: str, request: Request, user: dict = Depends(get_current_user), db: Session = Depends(get_db)):
|
||||
"""下载 dSYM 文件"""
|
||||
import shutil
|
||||
import zipfile
|
||||
@@ -255,6 +289,10 @@ async def download_dsym(task_id: str, db: Session = Depends(get_db)):
|
||||
raise HTTPException(status_code=404, detail="任务不存在")
|
||||
if not task.dsym_path:
|
||||
raise HTTPException(status_code=404, detail="dSYM 文件不存在")
|
||||
from ..services.audit_log import record_operation
|
||||
record_operation(db, user, "dsym_downloaded", request=request, task=task,
|
||||
resource_type="dsym", resource_name=os.path.basename(task.dsym_path))
|
||||
db.commit()
|
||||
|
||||
dsym_path = task.dsym_path
|
||||
if not os.path.exists(dsym_path):
|
||||
@@ -275,13 +313,17 @@ async def download_dsym(task_id: str, db: Session = Depends(get_db)):
|
||||
|
||||
|
||||
@router.get("/{task_id}/obfuscation-maps")
|
||||
async def download_obfuscation_maps(task_id: str, db: Session = Depends(get_db)):
|
||||
async def download_obfuscation_maps(task_id: str, request: Request, user: dict = Depends(get_current_user), db: Session = Depends(get_db)):
|
||||
"""下载混淆映射表"""
|
||||
task = db.query(Task).filter(Task.id == task_id).first()
|
||||
if not task:
|
||||
raise HTTPException(status_code=404, detail="任务不存在")
|
||||
if not task.obfuscation_maps_path:
|
||||
raise HTTPException(status_code=404, detail="混淆映射表不存在")
|
||||
from ..services.audit_log import record_operation
|
||||
record_operation(db, user, "obfuscation_maps_downloaded", request=request, task=task,
|
||||
resource_type="obfuscation_maps", resource_name=os.path.basename(task.obfuscation_maps_path))
|
||||
db.commit()
|
||||
|
||||
maps_path = task.obfuscation_maps_path
|
||||
if not os.path.exists(maps_path):
|
||||
|
||||
Reference in New Issue
Block a user