182 lines
5.9 KiB
Python
182 lines
5.9 KiB
Python
from typing import List, Optional
|
|
|
|
from bson import ObjectId
|
|
from fastapi import APIRouter, Depends, HTTPException, status
|
|
from pydantic import BaseModel
|
|
from api.dependency import get_dao
|
|
from api.endpoints.auth import get_current_user
|
|
from models.Project import Project
|
|
from repos.dao import DAO
|
|
|
|
router = APIRouter(prefix="/api/projects", tags=["Projects"])
|
|
|
|
class ProjectCreate(BaseModel):
|
|
name: str
|
|
description: Optional[str] = None
|
|
|
|
class ProjectMemberResponse(BaseModel):
|
|
id: str
|
|
username: str
|
|
|
|
class ProjectResponse(BaseModel):
|
|
id: str
|
|
name: str
|
|
description: Optional[str] = None
|
|
owner_id: str
|
|
members: List[ProjectMemberResponse]
|
|
is_owner: bool = False
|
|
|
|
async def _get_project_response(project: Project, current_user_id: str, dao: DAO) -> ProjectResponse:
|
|
member_responses = []
|
|
for member_id in project.members:
|
|
# We need a way to get user by ID. Let's check UsersRepo for get_user by ObjectId or string.
|
|
# Currently UsersRepo has get_user(user_id: int) for Telegram IDs.
|
|
# But for Web users we might need to search by _id.
|
|
# Let's try to get user info.
|
|
# Since project.members contains strings (ObjectIds as strings), we search by _id.
|
|
user_doc = await dao.users.collection.find_one({"_id": ObjectId(member_id)})
|
|
if not user_doc and member_id.isdigit():
|
|
# Fallback for telegram IDs if they are stored as strings of digits
|
|
user_doc = await dao.users.get_user(int(member_id))
|
|
|
|
username = "unknown"
|
|
if user_doc:
|
|
username = user_doc.get("username", "unknown")
|
|
|
|
member_responses.append(ProjectMemberResponse(id=member_id, username=username))
|
|
|
|
return ProjectResponse(
|
|
id=project.id,
|
|
name=project.name,
|
|
description=project.description,
|
|
owner_id=project.owner_id,
|
|
members=member_responses,
|
|
is_owner=(project.owner_id == current_user_id)
|
|
)
|
|
|
|
@router.post("", response_model=ProjectResponse)
|
|
async def create_project(
|
|
project_data: ProjectCreate,
|
|
dao: DAO = Depends(get_dao),
|
|
current_user: dict = Depends(get_current_user)
|
|
):
|
|
user_id = str(current_user["_id"])
|
|
new_project = Project(
|
|
name=project_data.name,
|
|
description=project_data.description,
|
|
owner_id=user_id,
|
|
members=[user_id]
|
|
)
|
|
project_id = await dao.projects.create_project(new_project)
|
|
new_project.id = project_id
|
|
|
|
# Add project to user's project list
|
|
await dao.users.collection.update_one(
|
|
{"_id": current_user["_id"]},
|
|
{"$addToSet": {"project_ids": project_id}}
|
|
)
|
|
|
|
return await _get_project_response(new_project, user_id, dao)
|
|
|
|
@router.get("", response_model=List[ProjectResponse])
|
|
async def get_my_projects(
|
|
dao: DAO = Depends(get_dao),
|
|
current_user: dict = Depends(get_current_user)
|
|
):
|
|
user_id = str(current_user["_id"])
|
|
projects = await dao.projects.get_projects_by_user(user_id)
|
|
|
|
responses = []
|
|
for p in projects:
|
|
responses.append(await _get_project_response(p, user_id, dao))
|
|
return responses
|
|
|
|
class MemberAdd(BaseModel):
|
|
username: str
|
|
|
|
@router.post("/{project_id}/members", dependencies=[Depends(get_current_user)])
|
|
async def add_member(
|
|
project_id: str,
|
|
member_data: MemberAdd,
|
|
dao: DAO = Depends(get_dao),
|
|
current_user: dict = Depends(get_current_user)
|
|
):
|
|
user_id = str(current_user["_id"])
|
|
project = await dao.projects.get_project(project_id)
|
|
if not project:
|
|
raise HTTPException(status_code=404, detail="Project not found")
|
|
|
|
if project.owner_id != user_id:
|
|
raise HTTPException(status_code=403, detail="Only owner can add members")
|
|
|
|
target_user = await dao.users.get_user_by_username(member_data.username)
|
|
if not target_user:
|
|
raise HTTPException(status_code=404, detail="User not found")
|
|
|
|
target_user_id = str(target_user["_id"])
|
|
|
|
if target_user_id in project.members:
|
|
return {"message": "User already in project"}
|
|
|
|
await dao.projects.add_member(project_id, target_user_id)
|
|
|
|
# Update target user's project list
|
|
await dao.users.collection.update_one(
|
|
{"_id": target_user["_id"]},
|
|
{"$addToSet": {"project_ids": project_id}}
|
|
)
|
|
|
|
return {"message": "Member added"}
|
|
|
|
@router.post("/{project_id}/join", dependencies=[Depends(get_current_user)])
|
|
async def join_project(
|
|
project_id: str,
|
|
dao: DAO = Depends(get_dao),
|
|
current_user: dict = Depends(get_current_user)
|
|
):
|
|
# Retrieve project to verify it exists
|
|
project = await dao.projects.get_project(project_id)
|
|
if not project:
|
|
raise HTTPException(status_code=404, detail="Project not found")
|
|
|
|
user_id = str(current_user["_id"])
|
|
|
|
# Check if user is ALREADY in project
|
|
if user_id in project.members:
|
|
return {"message": "Already a member"}
|
|
|
|
# Add member
|
|
await dao.projects.add_member(project_id, user_id)
|
|
|
|
# Update user's project list
|
|
await dao.users.collection.update_one(
|
|
{"_id": current_user["_id"]},
|
|
{"$addToSet": {"project_ids": project_id}}
|
|
)
|
|
|
|
return {"message": "Joined project"}
|
|
|
|
|
|
@router.delete("/{project_id}", dependencies=[Depends(get_current_user)] )
|
|
async def delete_project(
|
|
project_id: str,
|
|
dao: DAO = Depends(get_dao),
|
|
current_user: dict = Depends(get_current_user)
|
|
):
|
|
user_id = str(current_user["_id"])
|
|
project = await dao.projects.get_project(project_id)
|
|
if not project:
|
|
raise HTTPException(status_code=404, detail="Project not found")
|
|
|
|
if project.owner_id != user_id:
|
|
raise HTTPException(status_code=403, detail="Only owner can delete project")
|
|
|
|
await dao.projects.delete_project(project_id)
|
|
|
|
# Remove project from user's project list
|
|
await dao.users.collection.update_one(
|
|
{"_id": current_user["_id"]},
|
|
{"$pull": {"project_ids": project_id}}
|
|
)
|
|
|
|
return {"message": "Project deleted"} |