diff options
Diffstat (limited to 'makima/src/server/handlers/files.rs')
| -rw-r--r-- | makima/src/server/handlers/files.rs | 207 |
1 files changed, 1 insertions, 206 deletions
diff --git a/makima/src/server/handlers/files.rs b/makima/src/server/handlers/files.rs index 711be41..023b9ff 100644 --- a/makima/src/server/handlers/files.rs +++ b/makima/src/server/handlers/files.rs @@ -145,26 +145,7 @@ pub async fn create_file( .into_response(); }; - // Verify the contract exists and belongs to the owner - match repository::get_contract_for_owner(pool, req.contract_id, auth.owner_id).await { - Ok(None) => { - return ( - StatusCode::NOT_FOUND, - Json(ApiError::new("CONTRACT_NOT_FOUND", "Contract not found")), - ) - .into_response(); - } - Err(e) => { - tracing::error!("Failed to verify contract: {}", e); - return ( - StatusCode::INTERNAL_SERVER_ERROR, - Json(ApiError::new("DB_ERROR", e.to_string())), - ) - .into_response(); - } - Ok(Some(_)) => {} // Contract exists, proceed - } - + // Legacy contract scope removed; files are owner-scoped only now. match repository::create_file_for_owner(pool, auth.owner_id, req).await { Ok(file) => (StatusCode::CREATED, Json(file)).into_response(), Err(e) => { @@ -336,189 +317,3 @@ pub async fn delete_file( } } -/// Sync a file from its linked repository file. -/// -/// This endpoint triggers an async sync operation. The file must have a -/// repo_file_path set, and its contract must have a linked repository. -/// A connected daemon will read the file and update the file content. -#[utoipa::path( - post, - path = "/api/v1/files/{id}/sync-from-repo", - params( - ("id" = Uuid, Path, description = "File ID") - ), - responses( - (status = 202, description = "Sync operation started"), - (status = 400, description = "File not linked to repository", body = ApiError), - (status = 401, description = "Unauthorized", body = ApiError), - (status = 404, description = "File not found", body = ApiError), - (status = 503, description = "No daemon available", body = ApiError), - (status = 500, description = "Internal server error", body = ApiError), - ), - security( - ("bearer_auth" = []), - ("api_key" = []) - ), - tag = "Files" -)] -pub async fn sync_file_from_repo( - State(state): State<SharedState>, - Authenticated(auth): Authenticated, - Path(id): Path<Uuid>, -) -> impl IntoResponse { - let Some(ref pool) = state.db_pool else { - return ( - StatusCode::SERVICE_UNAVAILABLE, - Json(ApiError::new("DB_UNAVAILABLE", "Database not configured")), - ) - .into_response(); - }; - - // Get the file and verify it has a repo_file_path - let file = match repository::get_file_for_owner(pool, id, auth.owner_id).await { - Ok(Some(f)) => f, - Ok(None) => { - return ( - StatusCode::NOT_FOUND, - Json(ApiError::new("NOT_FOUND", "File not found")), - ) - .into_response(); - } - Err(e) => { - tracing::error!("Failed to get file {}: {}", id, e); - return ( - StatusCode::INTERNAL_SERVER_ERROR, - Json(ApiError::new("DB_ERROR", e.to_string())), - ) - .into_response(); - } - }; - - // Check if file has a repo path and contract_id - let contract_id = match file.contract_id { - Some(id) => id, - None => { - return ( - StatusCode::BAD_REQUEST, - Json(ApiError::new( - "NO_CONTRACT", - "File is not associated with a contract", - )), - ) - .into_response(); - } - }; - - let repo_file_path = match file.repo_file_path { - Some(ref path) if !path.is_empty() => path.clone(), - _ => { - return ( - StatusCode::BAD_REQUEST, - Json(ApiError::new( - "NOT_LINKED", - "File is not linked to a repository file", - )), - ) - .into_response(); - } - }; - - // Get contract repositories - let repositories = match repository::list_contract_repositories(pool, contract_id).await { - Ok(repos) => repos, - Err(e) => { - tracing::error!("Failed to get contract repositories: {}", e); - return ( - StatusCode::INTERNAL_SERVER_ERROR, - Json(ApiError::new("DB_ERROR", e.to_string())), - ) - .into_response(); - } - }; - - // Check if contract has repositories - if repositories.is_empty() { - return ( - StatusCode::BAD_REQUEST, - Json(ApiError::new( - "NO_REPOSITORY", - "Contract has no linked repositories", - )), - ) - .into_response(); - } - - // Use the first repository's local path - let repo = &repositories[0]; - let repo_local_path = match &repo.local_path { - Some(path) if !path.is_empty() => path.clone(), - _ => { - return ( - StatusCode::BAD_REQUEST, - Json(ApiError::new( - "NO_LOCAL_PATH", - "Repository has no local path configured", - )), - ) - .into_response(); - } - }; - - // Find a connected daemon for this owner - let daemon_id = state - .daemon_connections - .iter() - .find(|entry| entry.value().owner_id == auth.owner_id) - .map(|entry| entry.value().id); - - let daemon_id = match daemon_id { - Some(id) => id, - None => { - return ( - StatusCode::SERVICE_UNAVAILABLE, - Json(ApiError::new( - "NO_DAEMON", - "No daemon connected. Start a daemon to sync files from repository.", - )), - ) - .into_response(); - } - }; - - // Send ReadRepoFile command to daemon - // Use the file ID as the request_id so we can match the response - let command = DaemonCommand::ReadRepoFile { - request_id: id, - contract_id, - file_path: repo_file_path, - repo_path: repo_local_path, - }; - - if let Err(e) = state.send_daemon_command(daemon_id, command).await { - tracing::error!("Failed to send ReadRepoFile command: {}", e); - return ( - StatusCode::INTERNAL_SERVER_ERROR, - Json(ApiError::new("DAEMON_ERROR", e)), - ) - .into_response(); - } - - // Update status to indicate sync in progress - if let Err(e) = sqlx::query("UPDATE files SET repo_sync_status = 'syncing' WHERE id = $1") - .bind(id) - .execute(pool) - .await - { - tracing::warn!("Failed to update repo_sync_status: {}", e); - } - - // Return 202 Accepted - the sync happens asynchronously - ( - StatusCode::ACCEPTED, - Json(serde_json::json!({ - "message": "Sync operation started", - "fileId": id, - })), - ) - .into_response() -} |
