diff options
| author | soryu <soryu@soryu.co> | 2026-05-18 01:21:30 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-05-18 01:21:30 +0100 |
| commit | f240675da99bc7705e473b8f70a2628812aa4c10 (patch) | |
| tree | 3ee2d24b431ccb8cd1a3013c86b34a5782a3e224 /makima/src/server/handlers/files.rs | |
| parent | 0d996cf7590e3e52f424859c7d6f0e68640f119e (diff) | |
| download | soryu-f240675da99bc7705e473b8f70a2628812aa4c10.tar.gz soryu-f240675da99bc7705e473b8f70a2628812aa4c10.zip | |
The contracts table, supervisor task type, and all their backing
machinery have been inert for several PRs. The directives system reads
its own active contract body for spec text, and PR #135 removed the
last LLM surface that spawned supervisors.
This PR wipes the dead surface in one shot — the user authorised a DB
wipe, so the migration drops every legacy table with CASCADE rather
than carrying forward stub rows. Net change: −12k LOC across handlers,
repository, state, models, the TUI, and the listen module.
What's gone:
- contracts, contract_chat_*, contract_events, contract_repositories,
contract_type_templates tables.
- supervisor_states, supervisor_heartbeats tables.
- mesh_chat_conversations, mesh_chat_messages tables.
- tasks.contract_id/is_supervisor/supervisor_task_id/supervisor_worktree_task_id columns.
- directive_steps.contract_id/contract_type columns.
- files.contract_id/contract_phase columns.
- history_events.contract_id/phase columns.
- The Contract/Supervisor/MeshChat handler + model + repository
surface, plus the daemon TUI views that read them.
- The standalone listen.rs websocket handler (orphaned with the LLM).
What stays:
- mesh_supervisor handler: trimmed to just the questions + orders
backchannel used by `makima directive ask` / `create-order` (kept
the URL prefix for CLI client compat).
- directive_documents (the user-facing "contracts" surface).
- pending_questions in-memory state for the directive Ask flow.
cargo check, cargo test --lib (68 passed), tsc, and vite build all
clean.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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() -} |
