nex_docus/backend/app/api/v1/git_repos.py

175 lines
5.8 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

"""
项目Git仓库管理 API
"""
from fastapi import APIRouter, Depends, HTTPException, Request
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select, update, func
from typing import List
from app.core.database import get_db
from app.core.deps import get_current_user
from app.models.user import User
from app.models.project import Project, ProjectMember
from app.models.git_repo import ProjectGitRepo
from app.schemas.git_repo import GitRepoCreate, GitRepoUpdate, GitRepoResponse
from app.schemas.response import success_response
from app.services.log_service import log_service
from app.core.enums import OperationType
router = APIRouter()
async def check_project_permission(db: AsyncSession, project_id: int, user_id: int, required_roles: list = None):
"""检查项目权限"""
# 查询项目
result = await db.execute(select(Project).where(Project.id == project_id))
project = result.scalar_one_or_none()
if not project:
raise HTTPException(status_code=404, detail="项目不存在")
# 如果是所有者,直接通过
if project.owner_id == user_id:
return project
# 如果指定了角色要求
if required_roles:
member_result = await db.execute(
select(ProjectMember).where(
ProjectMember.project_id == project_id,
ProjectMember.user_id == user_id,
ProjectMember.role.in_(required_roles)
)
)
if not member_result.scalar_one_or_none():
raise HTTPException(status_code=403, detail="无权执行此操作")
return project
@router.get("/projects/{project_id}/git-repos", response_model=dict)
async def get_project_git_repos(
project_id: int,
current_user: User = Depends(get_current_user),
db: AsyncSession = Depends(get_db)
):
"""获取项目的Git仓库列表"""
# 检查权限(查看权限即可)
# 这里稍微放宽一点只要能访问项目就能看Git配置
# 为了安全,还是限制为成员
await check_project_permission(db, project_id, current_user.id, ['admin', 'editor', 'viewer'])
result = await db.execute(
select(ProjectGitRepo).where(ProjectGitRepo.project_id == project_id).order_by(ProjectGitRepo.created_at)
)
repos = result.scalars().all()
# 隐藏 token
data = []
for repo in repos:
repo_dict = GitRepoResponse.from_orm(repo).dict()
# repo_dict['token'] = '******' if repo.token else None # 前端可能需要回显或者判断是否有token
data.append(repo_dict)
return success_response(data=data)
@router.post("/projects/{project_id}/git-repos", response_model=dict)
async def create_git_repo(
project_id: int,
repo_in: GitRepoCreate,
request: Request,
current_user: User = Depends(get_current_user),
db: AsyncSession = Depends(get_db)
):
"""添加Git仓库"""
await check_project_permission(db, project_id, current_user.id, ['admin', 'editor'])
# 如果是设为默认,先取消其他默认
if repo_in.is_default:
await db.execute(
update(ProjectGitRepo)
.where(ProjectGitRepo.project_id == project_id)
.values(is_default=0)
)
# 检查是否是第一个仓库,如果是,强制设为默认
result = await db.execute(select(func.count()).select_from(ProjectGitRepo).where(ProjectGitRepo.project_id == project_id))
count = result.scalar()
is_default = repo_in.is_default
if count == 0:
is_default = 1
db_repo = ProjectGitRepo(
project_id=project_id,
name=repo_in.name,
repo_url=repo_in.repo_url,
branch=repo_in.branch,
username=repo_in.username,
token=repo_in.token,
is_default=is_default
)
db.add(db_repo)
await db.commit()
await db.refresh(db_repo)
return success_response(data=GitRepoResponse.from_orm(db_repo).dict(), message="Git仓库添加成功")
@router.put("/projects/{project_id}/git-repos/{repo_id}", response_model=dict)
async def update_git_repo(
project_id: int,
repo_id: int,
repo_in: GitRepoUpdate,
request: Request,
current_user: User = Depends(get_current_user),
db: AsyncSession = Depends(get_db)
):
"""更新Git仓库"""
await check_project_permission(db, project_id, current_user.id, ['admin', 'editor'])
result = await db.execute(select(ProjectGitRepo).where(ProjectGitRepo.id == repo_id, ProjectGitRepo.project_id == project_id))
repo = result.scalar_one_or_none()
if not repo:
raise HTTPException(status_code=404, detail="仓库不存在")
# 如果设为默认,取消其他默认
if repo_in.is_default == 1:
await db.execute(
update(ProjectGitRepo)
.where(ProjectGitRepo.project_id == project_id)
.values(is_default=0)
)
update_data = repo_in.dict(exclude_unset=True)
for field, value in update_data.items():
setattr(repo, field, value)
await db.commit()
await db.refresh(repo)
return success_response(data=GitRepoResponse.from_orm(repo).dict(), message="更新成功")
@router.delete("/projects/{project_id}/git-repos/{repo_id}", response_model=dict)
async def delete_git_repo(
project_id: int,
repo_id: int,
current_user: User = Depends(get_current_user),
db: AsyncSession = Depends(get_db)
):
"""删除Git仓库"""
await check_project_permission(db, project_id, current_user.id, ['admin', 'editor'])
result = await db.execute(select(ProjectGitRepo).where(ProjectGitRepo.id == repo_id, ProjectGitRepo.project_id == project_id))
repo = result.scalar_one_or_none()
if not repo:
raise HTTPException(status_code=404, detail="仓库不存在")
await db.delete(repo)
await db.commit()
return success_response(message="删除成功")