summaryrefslogtreecommitdiff
path: root/makima/src/server/handlers
diff options
context:
space:
mode:
authorsoryu <soryu@soryu.co>2026-02-09 02:19:38 +0000
committersoryu <soryu@soryu.co>2026-02-09 02:19:38 +0000
commit9c92d9235a0d1258fff9f7e625b0463c4952c45f (patch)
tree408bb739c7e58e282d762631c7a812e4c96fd6e8 /makima/src/server/handlers
parent9eb28de59b3b3815fc0eb15b37efcb07d51b8e96 (diff)
downloadsoryu-9c92d9235a0d1258fff9f7e625b0463c4952c45f.tar.gz
soryu-9c92d9235a0d1258fff9f7e625b0463c4952c45f.zip
Resume contracts from patches
Diffstat (limited to 'makima/src/server/handlers')
-rw-r--r--makima/src/server/handlers/mesh.rs97
1 files changed, 97 insertions, 0 deletions
diff --git a/makima/src/server/handlers/mesh.rs b/makima/src/server/handlers/mesh.rs
index 310bec8..5572d95 100644
--- a/makima/src/server/handlers/mesh.rs
+++ b/makima/src/server/handlers/mesh.rs
@@ -2264,6 +2264,103 @@ pub async fn list_task_patches(
Json(summaries).into_response()
}
+/// Response containing the latest checkpoint patch data for a task.
+#[derive(Debug, serde::Serialize, utoipa::ToSchema)]
+#[serde(rename_all = "camelCase")]
+pub struct TaskPatchDataResponse {
+ /// Task ID
+ pub task_id: Uuid,
+ /// Base64-encoded patch data (gzip-compressed git diff)
+ pub patch_data: String,
+ /// The commit SHA that the patch should be applied on top of
+ pub base_commit_sha: String,
+ /// Repository URL from the task
+ pub repository_url: Option<String>,
+}
+
+/// Get the latest checkpoint patch data for a task (for worktree restoration).
+#[utoipa::path(
+ get,
+ path = "/api/v1/mesh/tasks/{id}/patch-data",
+ params(
+ ("id" = Uuid, Path, description = "Task ID")
+ ),
+ responses(
+ (status = 200, description = "Latest patch data", body = TaskPatchDataResponse),
+ (status = 401, description = "Unauthorized", body = ApiError),
+ (status = 404, description = "Task or patch not found", body = ApiError),
+ (status = 503, description = "Database not configured", body = ApiError),
+ ),
+ security(
+ ("bearer_auth" = []),
+ ("api_key" = [])
+ ),
+ tag = "Mesh"
+)]
+pub async fn get_task_patch_data(
+ 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 task (scoped by owner)
+ let task = match repository::get_task_for_owner(pool, id, auth.owner_id).await {
+ Ok(Some(t)) => t,
+ Ok(None) => {
+ return (
+ StatusCode::NOT_FOUND,
+ Json(ApiError::new("NOT_FOUND", "Task not found")),
+ )
+ .into_response();
+ }
+ Err(e) => {
+ tracing::error!("Failed to get task {}: {}", id, e);
+ return (
+ StatusCode::INTERNAL_SERVER_ERROR,
+ Json(ApiError::new("DB_ERROR", e.to_string())),
+ )
+ .into_response();
+ }
+ };
+
+ // Get latest checkpoint patch (with binary data)
+ let patch = match repository::get_latest_checkpoint_patch(pool, id).await {
+ Ok(Some(p)) => p,
+ Ok(None) => {
+ return (
+ StatusCode::NOT_FOUND,
+ Json(ApiError::new("NOT_FOUND", "No checkpoint patch found for this task")),
+ )
+ .into_response();
+ }
+ Err(e) => {
+ tracing::error!("Failed to get patch for task {}: {}", id, e);
+ return (
+ StatusCode::INTERNAL_SERVER_ERROR,
+ Json(ApiError::new("DB_ERROR", e.to_string())),
+ )
+ .into_response();
+ }
+ };
+
+ let patch_data_b64 = base64::engine::general_purpose::STANDARD.encode(&patch.patch_data);
+
+ Json(TaskPatchDataResponse {
+ task_id: id,
+ patch_data: patch_data_b64,
+ base_commit_sha: patch.base_commit_sha,
+ repository_url: task.repository_url,
+ })
+ .into_response()
+}
+
/// Request to check if a target directory exists.
#[derive(Debug, serde::Deserialize, utoipa::ToSchema)]
#[serde(rename_all = "camelCase")]