generalbots/botserver/src/docs/handlers_api/comments.rs
Rodrigo Rodriguez (Pragmatismo) 037db5c381 feat: Major workspace reorganization and documentation update
- Add comprehensive documentation in botbook/ with 12 chapters
- Add botapp/ Tauri desktop application
- Add botdevice/ IoT device support
- Add botlib/ shared library crate
- Add botmodels/ Python ML models service
- Add botplugin/ browser extension
- Add botserver/ reorganized server code
- Add bottemplates/ bot templates
- Add bottest/ integration tests
- Add botui/ web UI server
- Add CI/CD workflows in .forgejo/workflows/
- Add AGENTS.md and PROD.md documentation
- Add dependency management scripts (DEPENDENCIES.sh/ps1)
- Remove legacy src/ structure and migrations
- Clean up temporary and backup files
2026-04-19 08:14:25 -03:00

215 lines
6.8 KiB
Rust

use crate::core::shared::state::AppState;
use crate::docs::storage::{get_current_user_id, load_document_from_drive, save_document};
use crate::docs::types::{
AddCommentRequest, DeleteCommentRequest, DocumentComment, ListCommentsResponse,
ReplyCommentRequest, ResolveCommentRequest,
};
use axum::{
extract::{Query, State},
http::StatusCode,
Json,
};
use chrono::Utc;
use std::collections::HashMap;
use std::sync::Arc;
pub async fn handle_add_comment(
State(state): State<Arc<AppState>>,
Json(req): Json<AddCommentRequest>,
) -> Result<Json<serde_json::Value>, (StatusCode, Json<serde_json::Value>)> {
let user_id = get_current_user_id();
let mut doc = match load_document_from_drive(&state, &user_id, &req.doc_id).await {
Ok(Some(d)) => d,
Ok(None) => {
return Err((
StatusCode::NOT_FOUND,
Json(serde_json::json!({ "error": "Document not found" })),
))
}
Err(e) => {
return Err((
StatusCode::INTERNAL_SERVER_ERROR,
Json(serde_json::json!({ "error": e })),
))
}
};
let comment = DocumentComment {
id: uuid::Uuid::new_v4().to_string(),
author_id: user_id.clone(),
author_name: "User".to_string(),
content: req.content,
position: req.position,
length: req.length,
created_at: Utc::now(),
updated_at: Utc::now(),
replies: vec![],
resolved: false,
};
let comments = doc.comments.get_or_insert_with(Vec::new);
comments.push(comment.clone());
doc.updated_at = Utc::now();
if let Err(e) = save_document(&state, &user_id, &doc).await {
return Err((
StatusCode::INTERNAL_SERVER_ERROR,
Json(serde_json::json!({ "error": e })),
));
}
Ok(Json(serde_json::json!({ "success": true, "comment": comment })))
}
pub async fn handle_reply_comment(
State(state): State<Arc<AppState>>,
Json(req): Json<ReplyCommentRequest>,
) -> Result<Json<serde_json::Value>, (StatusCode, Json<serde_json::Value>)> {
let user_id = get_current_user_id();
let mut doc = match load_document_from_drive(&state, &user_id, &req.doc_id).await {
Ok(Some(d)) => d,
Ok(None) => {
return Err((
StatusCode::NOT_FOUND,
Json(serde_json::json!({ "error": "Document not found" })),
))
}
Err(e) => {
return Err((
StatusCode::INTERNAL_SERVER_ERROR,
Json(serde_json::json!({ "error": e })),
))
}
};
if let Some(comments) = &mut doc.comments {
for comment in comments.iter_mut() {
if comment.id == req.comment_id {
let reply = crate::docs::types::CommentReply {
id: uuid::Uuid::new_v4().to_string(),
author_id: user_id.clone(),
author_name: "User".to_string(),
content: req.content.clone(),
created_at: Utc::now(),
};
comment.replies.push(reply);
comment.updated_at = Utc::now();
break;
}
}
}
doc.updated_at = Utc::now();
if let Err(e) = save_document(&state, &user_id, &doc).await {
return Err((
StatusCode::INTERNAL_SERVER_ERROR,
Json(serde_json::json!({ "error": e })),
));
}
Ok(Json(serde_json::json!({ "success": true })))
}
pub async fn handle_resolve_comment(
State(state): State<Arc<AppState>>,
Json(req): Json<ResolveCommentRequest>,
) -> Result<Json<serde_json::Value>, (StatusCode, Json<serde_json::Value>)> {
let user_id = get_current_user_id();
let mut doc = match load_document_from_drive(&state, &user_id, &req.doc_id).await {
Ok(Some(d)) => d,
Ok(None) => {
return Err((
StatusCode::NOT_FOUND,
Json(serde_json::json!({ "error": "Document not found" })),
))
}
Err(e) => {
return Err((
StatusCode::INTERNAL_SERVER_ERROR,
Json(serde_json::json!({ "error": e })),
))
}
};
if let Some(comments) = &mut doc.comments {
for comment in comments.iter_mut() {
if comment.id == req.comment_id {
comment.resolved = req.resolved;
comment.updated_at = Utc::now();
break;
}
}
}
doc.updated_at = Utc::now();
if let Err(e) = save_document(&state, &user_id, &doc).await {
return Err((
StatusCode::INTERNAL_SERVER_ERROR,
Json(serde_json::json!({ "error": e })),
));
}
Ok(Json(serde_json::json!({ "success": true })))
}
pub async fn handle_delete_comment(
State(state): State<Arc<AppState>>,
Json(req): Json<DeleteCommentRequest>,
) -> Result<Json<serde_json::Value>, (StatusCode, Json<serde_json::Value>)> {
let user_id = get_current_user_id();
let mut doc = match load_document_from_drive(&state, &user_id, &req.doc_id).await {
Ok(Some(d)) => d,
Ok(None) => {
return Err((
StatusCode::NOT_FOUND,
Json(serde_json::json!({ "error": "Document not found" })),
))
}
Err(e) => {
return Err((
StatusCode::INTERNAL_SERVER_ERROR,
Json(serde_json::json!({ "error": e })),
))
}
};
if let Some(comments) = &mut doc.comments {
comments.retain(|c| c.id != req.comment_id);
}
doc.updated_at = Utc::now();
if let Err(e) = save_document(&state, &user_id, &doc).await {
return Err((
StatusCode::INTERNAL_SERVER_ERROR,
Json(serde_json::json!({ "error": e })),
));
}
Ok(Json(serde_json::json!({ "success": true })))
}
pub async fn handle_list_comments(
State(state): State<Arc<AppState>>,
Query(params): Query<HashMap<String, String>>,
) -> Result<Json<ListCommentsResponse>, (StatusCode, Json<serde_json::Value>)> {
let doc_id = params.get("doc_id").cloned().unwrap_or_default();
let user_id = get_current_user_id();
let doc = match load_document_from_drive(&state, &user_id, &doc_id).await {
Ok(Some(d)) => d,
Ok(None) => {
return Err((
StatusCode::NOT_FOUND,
Json(serde_json::json!({ "error": "Document not found" })),
))
}
Err(e) => {
return Err((
StatusCode::INTERNAL_SERVER_ERROR,
Json(serde_json::json!({ "error": e })),
))
}
};
let comments = doc.comments.unwrap_or_default();
Ok(Json(ListCommentsResponse { comments }))
}