summaryrefslogtreecommitdiff
path: root/makima/src/server/handlers/files.rs
diff options
context:
space:
mode:
Diffstat (limited to 'makima/src/server/handlers/files.rs')
-rw-r--r--makima/src/server/handlers/files.rs57
1 files changed, 53 insertions, 4 deletions
diff --git a/makima/src/server/handlers/files.rs b/makima/src/server/handlers/files.rs
index 746d66b..c65eed5 100644
--- a/makima/src/server/handlers/files.rs
+++ b/makima/src/server/handlers/files.rs
@@ -9,9 +9,9 @@ use axum::{
use uuid::Uuid;
use crate::db::models::{CreateFileRequest, FileListResponse, FileSummary, UpdateFileRequest};
-use crate::db::repository;
+use crate::db::repository::{self, RepositoryError};
use crate::server::messages::ApiError;
-use crate::server::state::SharedState;
+use crate::server::state::{FileUpdateNotification, SharedState};
/// List all files for the current owner.
#[utoipa::path(
@@ -148,6 +148,7 @@ pub async fn create_file(
responses(
(status = 200, description = "File updated", body = crate::db::models::File),
(status = 404, description = "File not found", body = ApiError),
+ (status = 409, description = "Version conflict", body = ApiError),
(status = 503, description = "Database not configured", body = ApiError),
(status = 500, description = "Internal server error", body = ApiError),
),
@@ -166,14 +167,62 @@ pub async fn update_file(
.into_response();
};
+ // Collect which fields are being updated for broadcast
+ let mut updated_fields = Vec::new();
+ if req.name.is_some() {
+ updated_fields.push("name".to_string());
+ }
+ if req.description.is_some() {
+ updated_fields.push("description".to_string());
+ }
+ if req.transcript.is_some() {
+ updated_fields.push("transcript".to_string());
+ }
+ if req.summary.is_some() {
+ updated_fields.push("summary".to_string());
+ }
+ if req.body.is_some() {
+ updated_fields.push("body".to_string());
+ }
+
match repository::update_file(pool, id, req).await {
- Ok(Some(file)) => Json(file).into_response(),
+ Ok(Some(file)) => {
+ // Broadcast update notification
+ state.broadcast_file_update(FileUpdateNotification {
+ file_id: id,
+ version: file.version,
+ updated_fields,
+ updated_by: "user".to_string(),
+ });
+ Json(file).into_response()
+ }
Ok(None) => (
StatusCode::NOT_FOUND,
Json(ApiError::new("NOT_FOUND", "File not found")),
)
.into_response(),
- Err(e) => {
+ Err(RepositoryError::VersionConflict { expected, actual }) => {
+ tracing::info!(
+ "Version conflict on file {}: expected {}, actual {}",
+ id,
+ expected,
+ actual
+ );
+ (
+ StatusCode::CONFLICT,
+ Json(serde_json::json!({
+ "code": "VERSION_CONFLICT",
+ "message": format!(
+ "File was modified by another user. Expected version {}, actual version {}",
+ expected, actual
+ ),
+ "expectedVersion": expected,
+ "actualVersion": actual,
+ })),
+ )
+ .into_response()
+ }
+ Err(RepositoryError::Database(e)) => {
tracing::error!("Failed to update file {}: {}", id, e);
(
StatusCode::INTERNAL_SERVER_ERROR,