| 219 | } |
| 220 | |
| 221 | async fn write_file(&self, input: &serde_json::Value) -> AppResult<String> { |
| 222 | let path_str = input["path"] |
| 223 | .as_str() |
| 224 | .ok_or_else(|| AppError::Validation("Missing 'path' field".to_string()))?; |
| 225 | let content = input["content"] |
| 226 | .as_str() |
| 227 | .ok_or_else(|| AppError::Validation("Missing 'content' field".to_string()))?; |
| 228 | let safe_path = self.validate_path(path_str)?; |
| 229 | |
| 230 | if let Some(parent) = safe_path.parent() { |
| 231 | tokio::fs::create_dir_all(parent) |
| 232 | .await |
| 233 | .map_err(AppError::from)?; |
| 234 | } |
| 235 | tokio::fs::write(safe_path, content) |
| 236 | .await |
| 237 | .map_err(AppError::from)?; |
| 238 | |
| 239 | Ok(format!("Written {} bytes to {}", content.len(), path_str)) |
| 240 | } |
| 241 | |
| 242 | async fn patch_file(&self, input: &serde_json::Value) -> AppResult<String> { |
| 243 | let path_str = input["path"] |