import logging
from typing import Annotated
from datetime import datetime, timezone
from fastapi import APIRouter, Request, Form
from fastapi.responses import RedirectResponse
from devplacepy.database import get_table, resolve_by_slug
from devplacepy.attachments import link_attachments, delete_target_attachments
from devplacepy.utils import generate_uid, require_user, create_mention_notifications, create_notification, award_badge
from devplacepy.models import CommentForm
logger = logging.getLogger(__name__)
router = APIRouter()
def resolve_target_redirect(target_type, target_uid):
if target_type == "post":
post = resolve_by_slug(get_table("posts"), target_uid)
return f"/posts/{post['slug'] or post['uid']}" if post else "/feed"
if target_type == "project":
project = resolve_by_slug(get_table("projects"), target_uid)
return f"/projects/{project['slug'] or project['uid']}" if project else "/projects"
if target_type == "news":
article = resolve_by_slug(get_table("news"), target_uid)
if article:
return f"/news/{article.get('slug', '') or article['uid']}"
return "/news"
if target_type == "bug":
return f"/bugs?highlight={target_uid}"
if target_type == "gist":
gist = resolve_by_slug(get_table("gists"), target_uid)
return f"/gists/{gist['slug'] or gist['uid']}" if gist else "/gists"
return "/bugs"
@router.post("/create")
async def create_comment(request: Request, data: Annotated[CommentForm, Form()]):
user = require_user(request)
content = data.content.strip()
target_uid = data.target_uid or data.post_uid
target_type = data.target_type
parent_uid = data.parent_uid
redirect_url = resolve_target_redirect(target_type, target_uid)
comment_uid = generate_uid()
insert = {
"uid": comment_uid,
"target_uid": target_uid,
"target_type": target_type,
"user_uid": user["uid"],
"content": content,
"parent_uid": parent_uid or None,
"created_at": datetime.now(timezone.utc).isoformat(),
}
if target_type == "post":
insert["post_uid"] = target_uid
get_table("comments").insert(insert)
if data.attachment_uids:
link_attachments(data.attachment_uids, "comment", comment_uid)
award_badge(user["uid"], "First Comment")
if target_type == "post":
if parent_uid:
parent = get_table("comments").find_one(uid=parent_uid)
if parent and parent["user_uid"] != user["uid"]:
create_notification(parent["user_uid"], "reply", f"{user['username']} replied to your comment", user["uid"])
else:
posts = get_table("posts")
post = posts.find_one(uid=target_uid)
if not post:
post = posts.find_one(slug=target_uid)
if post and post["user_uid"] != user["uid"]:
create_notification(post["user_uid"], "comment", f"{user['username']} commented on your post", user["uid"])
create_mention_notifications(content, user["uid"], redirect_url)
logger.info(f"Comment by {user['username']} on {target_type} {target_uid}")
return RedirectResponse(url=redirect_url, status_code=302)
@router.post("/delete/{comment_uid}")
async def delete_comment(request: Request, comment_uid: str):
user = require_user(request)
comments = get_table("comments")
comment = comments.find_one(uid=comment_uid)
if not comment:
return RedirectResponse(url="/feed", status_code=302)
if comment["user_uid"] != user["uid"]:
return RedirectResponse(url="/feed", status_code=302)
target_type = comment.get("target_type", "post")
target_uid = comment.get("target_uid") or comment.get("post_uid", "")
delete_target_attachments("comment", comment_uid)
get_table("votes").delete(target_uid=comment_uid, target_type="comment")
comments.delete(id=comment["id"])
logger.info(f"Comment {comment_uid} deleted by {user['username']}")
redirect_url = resolve_target_redirect(target_type, target_uid)
return RedirectResponse(url=redirect_url, status_code=302)