mirror of https://github.com/buster-so/buster.git
send up some changes to handlers
This commit is contained in:
parent
8232f628ea
commit
8d50adce47
|
@ -43,7 +43,6 @@ let mut conn = get_pg_pool().get().await?;
|
|||
### Concurrency Guidelines
|
||||
- Prioritize concurrent operations, especially for:
|
||||
- API requests
|
||||
- Database transactions
|
||||
- File operations
|
||||
- Optimize database connection usage:
|
||||
- Batch operations where possible
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
---
|
||||
description: This is helpul docs for buildng hanlders in the project.
|
||||
globs: ibs/handlers/**/*.rs
|
||||
description: This is helpul docs for buildng hanlders in the project.l
|
||||
globs: libs/handlers/**/*.rs
|
||||
alwaysApply: false
|
||||
---
|
||||
# Handler Rules and Best Practices
|
||||
|
@ -59,7 +59,6 @@ match operation() {
|
|||
### Database Operations
|
||||
- Use the connection pool: `get_pg_pool().get().await?`
|
||||
- Run concurrent operations when possible
|
||||
- Use transactions for related operations
|
||||
- Handle database-specific errors appropriately
|
||||
- Example:
|
||||
```rust
|
||||
|
|
|
@ -0,0 +1,106 @@
|
|||
use anyhow::Result;
|
||||
use chrono::Utc;
|
||||
use database::models::{Chat, User};
|
||||
use database::pool::get_pg_pool;
|
||||
use diesel::prelude::*;
|
||||
use diesel_async::RunQueryDsl;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashSet;
|
||||
use uuid::Uuid;
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct ChatDeleteResult {
|
||||
pub id: Uuid,
|
||||
pub success: bool,
|
||||
pub error: Option<String>,
|
||||
}
|
||||
|
||||
/// Bulk delete chats (soft delete by setting deleted_at)
|
||||
///
|
||||
/// This function efficiently soft deletes multiple chats using a bulk update operation.
|
||||
/// It validates that the user has permission to delete each chat (they must be the creator).
|
||||
///
|
||||
/// Returns a list of results indicating success or failure for each chat.
|
||||
pub async fn delete_chats_handler(
|
||||
chat_ids: Vec<Uuid>,
|
||||
user_id: &Uuid,
|
||||
) -> Result<Vec<ChatDeleteResult>> {
|
||||
use database::schema::chats;
|
||||
|
||||
// If no chat IDs provided, return empty result
|
||||
if chat_ids.is_empty() {
|
||||
return Ok(Vec::new());
|
||||
}
|
||||
|
||||
let pool = get_pg_pool();
|
||||
let mut conn = pool.get().await?;
|
||||
|
||||
// Find all chats that the user has permission to delete in one query
|
||||
let user_chats: Vec<Chat> = chats::table
|
||||
.filter(chats::id.eq_any(chat_ids.clone()))
|
||||
.filter(chats::created_by.eq(user_id))
|
||||
.filter(chats::deleted_at.is_null())
|
||||
.load::<Chat>(&mut conn)
|
||||
.await?;
|
||||
|
||||
// Create a set of authorized chat IDs for quick lookup
|
||||
let authorized_chat_ids: HashSet<Uuid> =
|
||||
user_chats.iter().map(|c| c.id).collect();
|
||||
|
||||
// Prepare results for unauthorized chats
|
||||
let mut delete_results: Vec<ChatDeleteResult> = chat_ids
|
||||
.iter()
|
||||
.filter(|id| !authorized_chat_ids.contains(id))
|
||||
.map(|id| ChatDeleteResult {
|
||||
id: *id,
|
||||
success: false,
|
||||
error: Some("Chat not found or you don't have permission to delete it".to_string()),
|
||||
})
|
||||
.collect();
|
||||
|
||||
// If we have authorized deletes, perform them in bulk
|
||||
if !authorized_chat_ids.is_empty() {
|
||||
let authorized_ids: Vec<Uuid> = authorized_chat_ids.into_iter().collect();
|
||||
|
||||
// Perform a bulk update for all authorized chats at once
|
||||
let result = diesel::update(chats::table)
|
||||
.filter(chats::id.eq_any(authorized_ids.clone()))
|
||||
.set((
|
||||
chats::deleted_at.eq(Some(Utc::now())),
|
||||
chats::updated_at.eq(Utc::now()),
|
||||
))
|
||||
.execute(&mut conn)
|
||||
.await;
|
||||
|
||||
match result {
|
||||
Ok(_) => {
|
||||
// Add success results for all authorized chats
|
||||
let success_results: Vec<ChatDeleteResult> = authorized_ids
|
||||
.into_iter()
|
||||
.map(|id| ChatDeleteResult {
|
||||
id,
|
||||
success: true,
|
||||
error: None,
|
||||
})
|
||||
.collect();
|
||||
|
||||
delete_results.extend(success_results);
|
||||
},
|
||||
Err(e) => {
|
||||
// Add error results for all authorized chats
|
||||
let error_results: Vec<ChatDeleteResult> = authorized_ids
|
||||
.into_iter()
|
||||
.map(|id| ChatDeleteResult {
|
||||
id,
|
||||
success: false,
|
||||
error: Some(format!("Failed to delete chat: {}", e)),
|
||||
})
|
||||
.collect();
|
||||
|
||||
delete_results.extend(error_results);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(delete_results)
|
||||
}
|
|
@ -1,9 +1,13 @@
|
|||
pub mod get_chat_handler;
|
||||
pub mod post_chat_handler;
|
||||
pub mod update_chats_handler;
|
||||
pub mod delete_chats_handler;
|
||||
pub mod types;
|
||||
pub mod streaming_parser;
|
||||
|
||||
pub use get_chat_handler::get_chat_handler;
|
||||
pub use post_chat_handler::post_chat_handler;
|
||||
pub use update_chats_handler::update_chats_handler;
|
||||
pub use delete_chats_handler::delete_chats_handler;
|
||||
pub use types::*;
|
||||
pub use streaming_parser::StreamingParser;
|
|
@ -0,0 +1,99 @@
|
|||
use anyhow::{anyhow, Result};
|
||||
use chrono::Utc;
|
||||
use database::models::{Chat, User};
|
||||
use database::pool::get_pg_pool;
|
||||
use diesel::prelude::*;
|
||||
use diesel::pg::expression::dsl::any;
|
||||
use diesel_async::RunQueryDsl;
|
||||
use futures::future::try_join_all;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashSet;
|
||||
use uuid::Uuid;
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct ChatUpdate {
|
||||
pub id: Uuid,
|
||||
pub title: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct ChatUpdateResult {
|
||||
pub id: Uuid,
|
||||
pub success: bool,
|
||||
pub error: Option<String>,
|
||||
}
|
||||
|
||||
/// Bulk update chat titles
|
||||
///
|
||||
/// This function efficiently updates the titles of multiple chats.
|
||||
/// It validates that the user has permission to update each chat (they must be the creator)
|
||||
/// in a single database query, then performs individual updates for each chat.
|
||||
///
|
||||
/// Returns a list of results indicating success or failure for each chat.
|
||||
pub async fn update_chats_handler(
|
||||
updates: Vec<ChatUpdate>,
|
||||
user_id: &Uuid,
|
||||
) -> Result<Vec<ChatUpdateResult>> {
|
||||
use database::schema::chats;
|
||||
|
||||
// If no updates provided, return empty result
|
||||
if updates.is_empty() {
|
||||
return Ok(Vec::new());
|
||||
}
|
||||
|
||||
let pool = get_pg_pool();
|
||||
let mut conn = pool.get().await?;
|
||||
|
||||
// Extract all chat IDs
|
||||
let chat_ids: Vec<Uuid> = updates.iter().map(|u| u.id).collect();
|
||||
|
||||
// Find all chats that the user has permission to update in one query
|
||||
let user_chats: Vec<Chat> = chats::table
|
||||
.filter(chats::id.eq(any(chat_ids.clone())))
|
||||
.filter(chats::created_by.eq(user_id))
|
||||
.filter(chats::deleted_at.is_null())
|
||||
.load::<Chat>(&mut conn)
|
||||
.await?;
|
||||
|
||||
// Create a set of authorized chat IDs for quick lookup
|
||||
let authorized_chat_ids: HashSet<Uuid> =
|
||||
user_chats.iter().map(|c| c.id).collect();
|
||||
|
||||
let mut update_results = Vec::with_capacity(updates.len());
|
||||
|
||||
// Process each update
|
||||
for update in updates {
|
||||
if authorized_chat_ids.contains(&update.id) {
|
||||
// Update the chat title
|
||||
let result = diesel::update(chats::table)
|
||||
.filter(chats::id.eq(update.id))
|
||||
.set((
|
||||
chats::title.eq(update.title.clone()),
|
||||
chats::updated_at.eq(Utc::now()),
|
||||
))
|
||||
.execute(&mut conn)
|
||||
.await;
|
||||
|
||||
match result {
|
||||
Ok(_) => update_results.push(ChatUpdateResult {
|
||||
id: update.id,
|
||||
success: true,
|
||||
error: None,
|
||||
}),
|
||||
Err(e) => update_results.push(ChatUpdateResult {
|
||||
id: update.id,
|
||||
success: false,
|
||||
error: Some(format!("Failed to update chat: {}", e)),
|
||||
}),
|
||||
}
|
||||
} else {
|
||||
update_results.push(ChatUpdateResult {
|
||||
id: update.id,
|
||||
success: false,
|
||||
error: Some("Chat not found or you don't have permission to update it".to_string()),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Ok(update_results)
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
use anyhow::Result;
|
||||
use axum::http::StatusCode;
|
||||
use axum::Extension;
|
||||
use axum::Json;
|
||||
use database::models::User;
|
||||
use handlers::chats::delete_chats_handler::{ChatDeleteResult};
|
||||
use handlers::chats::delete_chats_handler;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::routes::rest::ApiResponse;
|
||||
|
||||
pub async fn delete_chats_route(
|
||||
Extension(user): Extension<User>,
|
||||
Json(chat_ids): Json<Vec<Uuid>>,
|
||||
) -> Result<ApiResponse<Vec<ChatDeleteResult>>, (StatusCode, &'static str)> {
|
||||
match delete_chats_handler(chat_ids, &user.id).await {
|
||||
Ok(results) => Ok(ApiResponse::JsonData(results)),
|
||||
Err(e) => {
|
||||
tracing::error!("Error deleting chats: {}", e);
|
||||
Err((StatusCode::INTERNAL_SERVER_ERROR, "Failed to delete chats"))
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,13 +1,17 @@
|
|||
use axum::{
|
||||
routing::{get, post},
|
||||
routing::{get, post, put, delete},
|
||||
Router,
|
||||
};
|
||||
|
||||
mod get_chat;
|
||||
mod post_chat;
|
||||
mod update_chats;
|
||||
mod delete_chats;
|
||||
|
||||
pub fn router() -> Router {
|
||||
Router::new()
|
||||
.route("/", post(post_chat::post_chat_route))
|
||||
.route("/", put(update_chats::update_chats_route))
|
||||
.route("/", delete(delete_chats::delete_chats_route))
|
||||
.route("/:id", get(get_chat::get_chat_route))
|
||||
}
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
use anyhow::Result;
|
||||
use axum::http::StatusCode;
|
||||
use axum::Extension;
|
||||
use axum::Json;
|
||||
use database::models::User;
|
||||
use handlers::chats::update_chats_handler::{ChatUpdate, ChatUpdateResult};
|
||||
use handlers::chats::update_chats_handler;
|
||||
|
||||
use crate::routes::rest::ApiResponse;
|
||||
|
||||
pub async fn update_chats_route(
|
||||
Extension(user): Extension<User>,
|
||||
Json(updates): Json<Vec<ChatUpdate>>,
|
||||
) -> Result<ApiResponse<Vec<ChatUpdateResult>>, (StatusCode, &'static str)> {
|
||||
match update_chats_handler(updates, &user.id).await {
|
||||
Ok(results) => Ok(ApiResponse::JsonData(results)),
|
||||
Err(e) => {
|
||||
tracing::error!("Error updating chats: {}", e);
|
||||
Err((StatusCode::INTERNAL_SERVER_ERROR, "Failed to update chats"))
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue