Files

107 lines
4.4 KiB
Python

"""Comments router with RBAC and notifications."""
from typing import List
from fastapi import APIRouter, Depends, HTTPException, status
from sqlalchemy.orm import Session
from app.core.config import get_db
from app.models import models
from app.models.task import Task
from app.schemas import schemas
from app.api.deps import get_current_user_or_apikey
from app.api.rbac import check_project_role
from app.models.notification import Notification as NotificationModel
router = APIRouter(tags=["Comments"])
def _notify_if_needed(db, task_id, user_ids, ntype, title):
"""Helper to notify multiple users."""
task = db.query(Task).filter(Task.id == task_id).first()
if not task:
return
for uid in set(user_ids):
if uid:
n = NotificationModel(user_id=uid, type=ntype, title=title, entity_type="task", entity_id=task_id)
db.add(n)
db.commit()
@router.post("/comments", response_model=schemas.CommentResponse, status_code=status.HTTP_201_CREATED)
def create_comment(comment: schemas.CommentCreate, db: Session = Depends(get_db), current_user: models.User = Depends(get_current_user_or_apikey)):
task = db.query(Task).filter(Task.id == comment.task_id).first()
if not task:
raise HTTPException(status_code=404, detail="Task not found")
check_project_role(db, current_user.id, task.project_id, min_role="viewer")
db_comment = models.Comment(**comment.model_dump())
db.add(db_comment)
db.commit()
db.refresh(db_comment)
# Notify reporter and assignee (but not the commenter themselves)
notify_users = []
if task.reporter_id != current_user.id:
notify_users.append(task.reporter_id)
if task.assignee_id and task.assignee_id != current_user.id:
notify_users.append(task.assignee_id)
if notify_users:
_notify_if_needed(db, task.id, notify_users, "comment_added", f"New comment on: {task.title[:50]}")
return db_comment
@router.get("/tasks/{task_id}/comments")
def list_comments(task_id: str, db: Session = Depends(get_db)):
"""List comments for a task. task_id can be numeric id or task_code."""
try:
tid = int(task_id)
except (ValueError, TypeError):
task = db.query(Task).filter(Task.task_code == task_id).first()
if not task:
raise HTTPException(status_code=404, detail="Task not found")
tid = task.id
comments = db.query(models.Comment).filter(models.Comment.task_id == tid).all()
result = []
for c in comments:
author = db.query(models.User).filter(models.User.id == c.author_id).first()
result.append({
"id": c.id,
"content": c.content,
"task_id": c.task_id,
"author_id": c.author_id,
"author_username": author.username if author else None,
"created_at": c.created_at,
"updated_at": c.updated_at,
})
return result
@router.patch("/comments/{comment_id}", response_model=schemas.CommentResponse)
def update_comment(comment_id: int, comment_update: schemas.CommentUpdate, db: Session = Depends(get_db), current_user: models.User = Depends(get_current_user_or_apikey)):
comment = db.query(models.Comment).filter(models.Comment.id == comment_id).first()
if not comment:
raise HTTPException(status_code=404, detail="Comment not found")
task = db.query(Task).filter(Task.id == comment.task_id).first()
if not task:
raise HTTPException(status_code=404, detail="Task not found")
check_project_role(db, current_user.id, task.project_id, min_role="viewer")
for field, value in comment_update.model_dump(exclude_unset=True).items():
setattr(comment, field, value)
db.commit()
db.refresh(comment)
return comment
@router.delete("/comments/{comment_id}", status_code=status.HTTP_204_NO_CONTENT)
def delete_comment(comment_id: int, db: Session = Depends(get_db), current_user: models.User = Depends(get_current_user_or_apikey)):
comment = db.query(models.Comment).filter(models.Comment.id == comment_id).first()
if not comment:
raise HTTPException(status_code=404, detail="Comment not found")
task = db.query(Task).filter(Task.id == comment.task_id).first()
if not task:
raise HTTPException(status_code=404, detail="Task not found")
check_project_role(db, current_user.id, task.project_id, min_role="dev")
db.delete(comment)
db.commit()
return None