sync: Auto-sync from DESKTOP-0O8A1RL at 2026-04-02 19:20:43

Synced files:
- Session logs updated
- Latest context and credentials
- Command/directive updates

Machine: DESKTOP-0O8A1RL
Timestamp: 2026-04-02 19:20:43

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-04-02 19:20:43 -07:00
parent 6e4ebc2db9
commit bff7d9dbbf
8 changed files with 981 additions and 27 deletions

View File

@@ -160,3 +160,100 @@ pub async fn get_command(
Ok(Json(command))
}
/// Delete a single command by ID
/// Requires authentication.
pub async fn delete_command(
State(state): State<AppState>,
_user: AuthUser,
Path(command_id): Path<Uuid>,
) -> Result<StatusCode, (StatusCode, String)> {
let deleted = db::delete_command(&state.db, command_id)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
if deleted {
Ok(StatusCode::NO_CONTENT)
} else {
Err((StatusCode::NOT_FOUND, "Command not found".to_string()))
}
}
/// Cancel response payload
#[derive(Debug, Serialize)]
pub struct CancelCommandResponse {
pub status: String,
pub message: String,
}
/// Cancel a pending or running command
/// Requires authentication.
pub async fn cancel_command(
State(state): State<AppState>,
_user: AuthUser,
Path(command_id): Path<Uuid>,
) -> Result<Json<CancelCommandResponse>, (StatusCode, String)> {
let command = db::get_command_by_id(&state.db, command_id)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?
.ok_or((StatusCode::NOT_FOUND, "Command not found".to_string()))?;
match command.status.as_str() {
"completed" | "failed" | "cancelled" => {
return Err((
StatusCode::BAD_REQUEST,
"Command already finished".to_string(),
));
}
"running" => {
// Update status in DB
db::cancel_command(&state.db, command_id)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
// Optionally try to send a cancel signal via WebSocket
let agents = state.agents.read().await;
if agents.is_connected(&command.agent_id) {
let cancel_msg = ServerMessage::Error {
code: "command_cancelled".to_string(),
message: format!("Command {} has been cancelled", command_id),
};
let _ = agents.send_to(&command.agent_id, cancel_msg).await;
}
}
_ => {
// Pending or any other status - just update DB
db::cancel_command(&state.db, command_id)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
}
}
Ok(Json(CancelCommandResponse {
status: "cancelled".to_string(),
message: "Command cancelled".to_string(),
}))
}
/// Clear history response payload
#[derive(Debug, Serialize)]
pub struct ClearHistoryResponse {
pub deleted: u64,
pub message: String,
}
/// Bulk clear finished commands (completed, failed, cancelled)
/// Requires authentication.
pub async fn clear_command_history(
State(state): State<AppState>,
_user: AuthUser,
) -> Result<Json<ClearHistoryResponse>, (StatusCode, String)> {
let count = db::delete_finished_commands(&state.db)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
Ok(Json(ClearHistoryResponse {
deleted: count,
message: format!("Cleared {} commands from history", count),
}))
}

View File

@@ -56,8 +56,9 @@ pub fn routes() -> Router<AppState> {
.route("/metrics/summary", get(metrics::get_summary))
// Commands
.route("/agents/:id/command", post(commands::send_command))
.route("/commands", get(commands::list_commands))
.route("/commands/:id", get(commands::get_command))
.route("/commands", get(commands::list_commands).delete(commands::clear_command_history))
.route("/commands/:id", get(commands::get_command).delete(commands::delete_command))
.route("/commands/:id/cancel", post(commands::cancel_command))
// Legacy Agent (PowerShell for 2008 R2)
.route("/agent/register-legacy", post(agents::register_legacy))
.route("/agent/heartbeat", post(agents::heartbeat))