error handling, coderabbit
This commit is contained in:
33 files changed
+393
-298
No files matched your search
@@ -10,7 +10,7 @@ use serde::Deserialize;
|
||||
use tokio::time::Duration;
|
||||
use tracing::warn;
|
||||
|
||||
use crate::api::json_error;
|
||||
use crate::api::ApiError;
|
||||
use crate::runtime::AppState;
|
||||
use crate::state::{AlertState, AuditEvent, AuditEventFilter};
|
||||
|
||||
@@ -51,7 +51,7 @@ pub struct AlertRuleConfig {
|
||||
pub async fn active_alerts(
|
||||
State(state): State<AppState>,
|
||||
Query(query): Query<ActiveAlertsQuery>,
|
||||
) -> Result<impl IntoResponse, (StatusCode, Json<serde_json::Value>)> {
|
||||
) -> Result<impl IntoResponse, ApiError> {
|
||||
let alerts = evaluate_alerts(
|
||||
&state,
|
||||
AlertRuleConfig {
|
||||
@@ -73,7 +73,7 @@ pub async fn active_alerts(
|
||||
pub async fn alert_history(
|
||||
State(state): State<AppState>,
|
||||
Query(query): Query<AlertHistoryQuery>,
|
||||
) -> Result<impl IntoResponse, (StatusCode, Json<serde_json::Value>)> {
|
||||
) -> Result<impl IntoResponse, ApiError> {
|
||||
let limit = query.limit.unwrap_or(100).clamp(1, 500);
|
||||
let history = state
|
||||
.store
|
||||
@@ -81,7 +81,7 @@ pub async fn alert_history(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
warn!(error = %err, "failed reading alert transition history");
|
||||
json_error(
|
||||
ApiError::new(
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
"alert_history_failed",
|
||||
&err.to_string(),
|
||||
@@ -112,7 +112,7 @@ async fn alerts_stream_socket(state: AppState, mut socket: WebSocket, config: Al
|
||||
let alerts = match evaluate_alerts(&state, config.clone()).await {
|
||||
Ok(alerts) => alerts,
|
||||
Err(err) => {
|
||||
warn!(code = %err.0, "failed to evaluate alerts for stream");
|
||||
warn!(code = %err.code, "failed to evaluate alerts for stream");
|
||||
continue;
|
||||
}
|
||||
};
|
||||
@@ -152,7 +152,7 @@ async fn alerts_stream_socket(state: AppState, mut socket: WebSocket, config: Al
|
||||
async fn evaluate_alerts(
|
||||
state: &AppState,
|
||||
config: AlertRuleConfig,
|
||||
) -> Result<Vec<AlertState>, (StatusCode, Json<serde_json::Value>)> {
|
||||
) -> Result<Vec<AlertState>, ApiError> {
|
||||
let lookback_seconds = config.lookback_seconds.unwrap_or(900).clamp(60, 86_400);
|
||||
let timeout_threshold = config.timeout_threshold.unwrap_or(3).max(1);
|
||||
let auth_rejected_threshold = config.auth_rejected_threshold.unwrap_or(3).max(1);
|
||||
@@ -182,7 +182,7 @@ async fn evaluate_alerts(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
warn!(error = %err, "failed reading timeout audit events");
|
||||
json_error(
|
||||
ApiError::new(
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
"alerts_query_failed",
|
||||
&err.to_string(),
|
||||
@@ -201,7 +201,7 @@ async fn evaluate_alerts(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
warn!(error = %err, "failed reading auth-rejected audit events");
|
||||
json_error(
|
||||
ApiError::new(
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
"alerts_query_failed",
|
||||
&err.to_string(),
|
||||
@@ -220,7 +220,7 @@ async fn evaluate_alerts(
|
||||
.await
|
||||
.map_err(|err| {
|
||||
warn!(error = %err, "failed reading enroll-rejected audit events");
|
||||
json_error(
|
||||
ApiError::new(
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
"alerts_query_failed",
|
||||
&err.to_string(),
|
||||
|
||||
@@ -5,7 +5,7 @@ use axum::response::IntoResponse;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tracing::warn;
|
||||
|
||||
use crate::api::json_error;
|
||||
use crate::api::ApiError;
|
||||
use crate::runtime::AppState;
|
||||
use crate::state::AuditEventFilter;
|
||||
|
||||
@@ -38,7 +38,7 @@ pub struct AuditEventResponse {
|
||||
pub async fn list_audit_events(
|
||||
State(state): State<AppState>,
|
||||
Query(query): Query<ListAuditEventsQuery>,
|
||||
) -> Result<impl IntoResponse, (StatusCode, Json<serde_json::Value>)> {
|
||||
) -> Result<impl IntoResponse, ApiError> {
|
||||
let filter = AuditEventFilter {
|
||||
agent_id: query.agent_id,
|
||||
request_id: query.request_id,
|
||||
@@ -71,7 +71,7 @@ pub async fn list_audit_events(
|
||||
}
|
||||
Err(err) => {
|
||||
warn!(error = %err, "failed to list audit events");
|
||||
Err(json_error(
|
||||
Err(ApiError::new(
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
"list_audit_events_failed",
|
||||
&err.to_string(),
|
||||
|
||||
@@ -8,7 +8,7 @@ use tracing::{info, info_span, warn};
|
||||
use uuid::Uuid;
|
||||
use wakey_agent::protocol::{AgentCommand, ErrorPayload, RequestId, ServerMessage};
|
||||
|
||||
use crate::api::json_error;
|
||||
use crate::api::ApiError;
|
||||
use crate::runtime::{AgentReply, AppState, SessionEvent};
|
||||
use crate::state::AuditEventInput;
|
||||
|
||||
@@ -35,9 +35,7 @@ pub struct RelayCommandResponse {
|
||||
pub error: Option<ErrorPayload>,
|
||||
}
|
||||
|
||||
pub async fn list_agents(
|
||||
State(state): State<AppState>,
|
||||
) -> Result<impl IntoResponse, (StatusCode, Json<serde_json::Value>)> {
|
||||
pub async fn list_agents(State(state): State<AppState>) -> Result<impl IntoResponse, ApiError> {
|
||||
let enrolled = state.store.list_agents_with_nicknames().await;
|
||||
let sessions = state.sessions.read().await;
|
||||
|
||||
@@ -57,7 +55,7 @@ pub async fn run_command(
|
||||
State(state): State<AppState>,
|
||||
AxumPath(agent_id): AxumPath<String>,
|
||||
Json(req): Json<RelayCommandRequest>,
|
||||
) -> Result<impl IntoResponse, (StatusCode, Json<serde_json::Value>)> {
|
||||
) -> Result<impl IntoResponse, ApiError> {
|
||||
match relay_agent_command(&state, &agent_id, req.command, req.timeout_ms).await {
|
||||
Ok(response) => Ok((StatusCode::OK, Json(response))),
|
||||
Err(err) => Err(err),
|
||||
@@ -69,7 +67,7 @@ pub async fn relay_agent_command(
|
||||
agent_id: &str,
|
||||
command: AgentCommand,
|
||||
timeout_ms: Option<u64>,
|
||||
) -> Result<RelayCommandResponse, (StatusCode, Json<serde_json::Value>)> {
|
||||
) -> Result<RelayCommandResponse, ApiError> {
|
||||
let request_id_string = format!("req-{}", Uuid::new_v4());
|
||||
let command_kind = command_kind(&command);
|
||||
let started = Instant::now();
|
||||
@@ -82,7 +80,7 @@ pub async fn relay_agent_command(
|
||||
let _span_guard = span.enter();
|
||||
|
||||
let request_id = RequestId::try_from(request_id_string.clone()).map_err(|err| {
|
||||
json_error(
|
||||
ApiError::new(
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
"invalid_request_id",
|
||||
&err,
|
||||
@@ -95,7 +93,7 @@ pub async fn relay_agent_command(
|
||||
}
|
||||
.ok_or_else(|| {
|
||||
warn!("command rejected: agent not connected");
|
||||
json_error(
|
||||
ApiError::new(
|
||||
StatusCode::NOT_FOUND,
|
||||
"agent_not_connected",
|
||||
"agent is not connected",
|
||||
@@ -151,7 +149,7 @@ pub async fn relay_agent_command(
|
||||
{
|
||||
warn!(error = %audit_err, "failed to append audit event for command send failure");
|
||||
}
|
||||
return Err(json_error(
|
||||
return Err(ApiError::new(
|
||||
StatusCode::BAD_GATEWAY,
|
||||
"agent_send_failed",
|
||||
&format!("failed to send command to agent: {err}"),
|
||||
@@ -236,7 +234,7 @@ pub async fn relay_agent_command(
|
||||
{
|
||||
warn!(error = %err, "failed to append audit event for dropped response");
|
||||
}
|
||||
return Err(json_error(
|
||||
return Err(ApiError::new(
|
||||
StatusCode::BAD_GATEWAY,
|
||||
"agent_response_dropped",
|
||||
"agent response channel dropped",
|
||||
@@ -265,7 +263,7 @@ pub async fn relay_agent_command(
|
||||
{
|
||||
warn!(error = %err, "failed to append audit event for timeout");
|
||||
}
|
||||
return Err(json_error(
|
||||
return Err(ApiError::new(
|
||||
StatusCode::GATEWAY_TIMEOUT,
|
||||
"agent_timeout",
|
||||
"agent did not answer before timeout",
|
||||
|
||||
@@ -5,7 +5,7 @@ use axum::response::IntoResponse;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tracing::warn;
|
||||
|
||||
use crate::api::json_error;
|
||||
use crate::api::ApiError;
|
||||
use crate::runtime::AppState;
|
||||
use crate::state::{DeviceIdentifierInput, KnownDeviceInput};
|
||||
|
||||
@@ -59,7 +59,7 @@ pub struct MergeKnownDeviceRequest {
|
||||
pub async fn create_known_device(
|
||||
State(state): State<AppState>,
|
||||
Json(req): Json<CreateKnownDeviceRequest>,
|
||||
) -> Result<impl IntoResponse, (StatusCode, Json<serde_json::Value>)> {
|
||||
) -> Result<impl IntoResponse, ApiError> {
|
||||
let input = KnownDeviceInput {
|
||||
display_name: req.display_name,
|
||||
pinned: req.pinned,
|
||||
@@ -78,7 +78,7 @@ pub async fn create_known_device(
|
||||
Ok(device) => Ok((StatusCode::CREATED, Json(known_device_response(device)))),
|
||||
Err(err) => {
|
||||
warn!(error = %err, "failed to create known device");
|
||||
Err(json_error(
|
||||
Err(ApiError::new(
|
||||
StatusCode::BAD_REQUEST,
|
||||
"create_known_device_failed",
|
||||
&err.to_string(),
|
||||
@@ -89,7 +89,7 @@ pub async fn create_known_device(
|
||||
|
||||
pub async fn list_known_devices(
|
||||
State(state): State<AppState>,
|
||||
) -> Result<impl IntoResponse, (StatusCode, Json<serde_json::Value>)> {
|
||||
) -> Result<impl IntoResponse, ApiError> {
|
||||
match state.store.list_known_devices().await {
|
||||
Ok(devices) => Ok((
|
||||
StatusCode::OK,
|
||||
@@ -102,7 +102,7 @@ pub async fn list_known_devices(
|
||||
)),
|
||||
Err(err) => {
|
||||
warn!(error = %err, "failed to list known devices");
|
||||
Err(json_error(
|
||||
Err(ApiError::new(
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
"list_known_devices_failed",
|
||||
&err.to_string(),
|
||||
@@ -114,7 +114,7 @@ pub async fn list_known_devices(
|
||||
pub async fn forget_known_device(
|
||||
State(state): State<AppState>,
|
||||
AxumPath(device_id): AxumPath<String>,
|
||||
) -> Result<impl IntoResponse, (StatusCode, Json<serde_json::Value>)> {
|
||||
) -> Result<impl IntoResponse, ApiError> {
|
||||
match state.store.forget_known_device(&device_id).await {
|
||||
Ok(forgotten) => Ok((
|
||||
StatusCode::OK,
|
||||
@@ -125,7 +125,7 @@ pub async fn forget_known_device(
|
||||
)),
|
||||
Err(err) => {
|
||||
warn!(error = %err, "failed to forget known device");
|
||||
Err(json_error(
|
||||
Err(ApiError::new(
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
"forget_known_device_failed",
|
||||
&err.to_string(),
|
||||
@@ -138,7 +138,7 @@ pub async fn attach_device_identifier(
|
||||
State(state): State<AppState>,
|
||||
AxumPath(device_id): AxumPath<String>,
|
||||
Json(req): Json<DeviceIdentifierRequest>,
|
||||
) -> Result<impl IntoResponse, (StatusCode, Json<serde_json::Value>)> {
|
||||
) -> Result<impl IntoResponse, ApiError> {
|
||||
let input = DeviceIdentifierInput {
|
||||
kind: req.kind,
|
||||
value: req.value,
|
||||
@@ -150,14 +150,14 @@ pub async fn attach_device_identifier(
|
||||
.await
|
||||
{
|
||||
Ok(Some(device)) => Ok((StatusCode::OK, Json(known_device_response(device)))),
|
||||
Ok(None) => Err(json_error(
|
||||
Ok(None) => Err(ApiError::new(
|
||||
StatusCode::NOT_FOUND,
|
||||
"known_device_not_found",
|
||||
"known device not found",
|
||||
)),
|
||||
Err(err) => {
|
||||
warn!(error = %err, "failed to attach device identifier");
|
||||
Err(json_error(
|
||||
Err(ApiError::new(
|
||||
StatusCode::BAD_REQUEST,
|
||||
"attach_device_identifier_failed",
|
||||
&err.to_string(),
|
||||
@@ -169,21 +169,21 @@ pub async fn attach_device_identifier(
|
||||
pub async fn detach_device_identifier(
|
||||
State(state): State<AppState>,
|
||||
AxumPath((device_id, identifier_key)): AxumPath<(String, String)>,
|
||||
) -> Result<impl IntoResponse, (StatusCode, Json<serde_json::Value>)> {
|
||||
) -> Result<impl IntoResponse, ApiError> {
|
||||
match state
|
||||
.store
|
||||
.detach_device_identifier(&device_id, &identifier_key)
|
||||
.await
|
||||
{
|
||||
Ok(Some(device)) => Ok((StatusCode::OK, Json(known_device_response(device)))),
|
||||
Ok(None) => Err(json_error(
|
||||
Ok(None) => Err(ApiError::new(
|
||||
StatusCode::NOT_FOUND,
|
||||
"known_device_not_found",
|
||||
"known device not found",
|
||||
)),
|
||||
Err(err) => {
|
||||
warn!(error = %err, "failed to detach device identifier");
|
||||
Err(json_error(
|
||||
Err(ApiError::new(
|
||||
StatusCode::BAD_REQUEST,
|
||||
"detach_device_identifier_failed",
|
||||
&err.to_string(),
|
||||
@@ -196,21 +196,21 @@ pub async fn merge_known_device(
|
||||
State(state): State<AppState>,
|
||||
AxumPath(device_id): AxumPath<String>,
|
||||
Json(req): Json<MergeKnownDeviceRequest>,
|
||||
) -> Result<impl IntoResponse, (StatusCode, Json<serde_json::Value>)> {
|
||||
) -> Result<impl IntoResponse, ApiError> {
|
||||
match state
|
||||
.store
|
||||
.merge_known_devices(&device_id, &req.source_device_id)
|
||||
.await
|
||||
{
|
||||
Ok(Some(device)) => Ok((StatusCode::OK, Json(known_device_response(device)))),
|
||||
Ok(None) => Err(json_error(
|
||||
Ok(None) => Err(ApiError::new(
|
||||
StatusCode::NOT_FOUND,
|
||||
"known_device_not_found",
|
||||
"target or source known device not found",
|
||||
)),
|
||||
Err(err) => {
|
||||
warn!(error = %err, "failed to merge known devices");
|
||||
Err(json_error(
|
||||
Err(ApiError::new(
|
||||
StatusCode::BAD_REQUEST,
|
||||
"merge_known_device_failed",
|
||||
&err.to_string(),
|
||||
|
||||
@@ -5,7 +5,7 @@ use axum::response::IntoResponse;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tracing::{info, warn};
|
||||
|
||||
use crate::api::json_error;
|
||||
use crate::api::ApiError;
|
||||
use crate::runtime::{AppState, SessionEvent};
|
||||
use crate::state::AuditEventInput;
|
||||
|
||||
@@ -70,7 +70,7 @@ pub async fn healthz() -> &'static str {
|
||||
pub async fn enroll(
|
||||
State(state): State<AppState>,
|
||||
Json(req): Json<EnrollRequest>,
|
||||
) -> Result<impl IntoResponse, (StatusCode, Json<serde_json::Value>)> {
|
||||
) -> Result<impl IntoResponse, ApiError> {
|
||||
match state.store.enroll(&req.enroll_token).await {
|
||||
Ok(issued) => {
|
||||
info!(agent_id = %issued.agent_id, "agent enrollment accepted");
|
||||
@@ -119,7 +119,7 @@ pub async fn enroll(
|
||||
{
|
||||
warn!(error = %audit_err, "failed to append audit event for enroll rejection");
|
||||
}
|
||||
Err(json_error(
|
||||
Err(ApiError::new(
|
||||
StatusCode::UNAUTHORIZED,
|
||||
"enrollment_rejected",
|
||||
&err.to_string(),
|
||||
@@ -131,7 +131,7 @@ pub async fn enroll(
|
||||
pub async fn issue_enroll_token(
|
||||
State(state): State<AppState>,
|
||||
Query(query): Query<IssueEnrollTokenQuery>,
|
||||
) -> Result<impl IntoResponse, (StatusCode, Json<serde_json::Value>)> {
|
||||
) -> Result<impl IntoResponse, ApiError> {
|
||||
let ttl = std::time::Duration::from_secs(
|
||||
query
|
||||
.ttl_seconds
|
||||
@@ -175,7 +175,7 @@ pub async fn issue_enroll_token(
|
||||
}
|
||||
Err(err) => {
|
||||
warn!(error = %err, "failed to issue enroll token");
|
||||
Err(json_error(
|
||||
Err(ApiError::new(
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
"issue_enroll_token_failed",
|
||||
&err.to_string(),
|
||||
@@ -186,7 +186,7 @@ pub async fn issue_enroll_token(
|
||||
|
||||
pub async fn list_enroll_tokens(
|
||||
State(state): State<AppState>,
|
||||
) -> Result<impl IntoResponse, (StatusCode, Json<serde_json::Value>)> {
|
||||
) -> Result<impl IntoResponse, ApiError> {
|
||||
match state.store.list_enroll_tokens().await {
|
||||
Ok(tokens) => {
|
||||
if let Err(err) = state
|
||||
@@ -220,7 +220,7 @@ pub async fn list_enroll_tokens(
|
||||
}
|
||||
Err(err) => {
|
||||
warn!(error = %err, "failed to list enroll tokens");
|
||||
Err(json_error(
|
||||
Err(ApiError::new(
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
"list_enroll_tokens_failed",
|
||||
&err.to_string(),
|
||||
@@ -232,7 +232,7 @@ pub async fn list_enroll_tokens(
|
||||
pub async fn revoke_enroll_token(
|
||||
State(state): State<AppState>,
|
||||
AxumPath(token): AxumPath<String>,
|
||||
) -> Result<impl IntoResponse, (StatusCode, Json<serde_json::Value>)> {
|
||||
) -> Result<impl IntoResponse, ApiError> {
|
||||
match state.store.revoke_enroll_token(&token).await {
|
||||
Ok(revoked) => {
|
||||
if let Err(err) = state
|
||||
@@ -267,7 +267,7 @@ pub async fn revoke_enroll_token(
|
||||
}
|
||||
Err(err) => {
|
||||
warn!(error = %err, "failed to revoke enroll token");
|
||||
Err(json_error(
|
||||
Err(ApiError::new(
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
"revoke_enroll_token_failed",
|
||||
&err.to_string(),
|
||||
@@ -279,7 +279,7 @@ pub async fn revoke_enroll_token(
|
||||
pub async fn revoke_agent(
|
||||
State(state): State<AppState>,
|
||||
AxumPath(agent_id): AxumPath<String>,
|
||||
) -> Result<impl IntoResponse, (StatusCode, Json<serde_json::Value>)> {
|
||||
) -> Result<impl IntoResponse, ApiError> {
|
||||
match state.store.revoke_agent(&agent_id).await {
|
||||
Ok(revoked) => {
|
||||
if revoked {
|
||||
@@ -322,7 +322,7 @@ pub async fn revoke_agent(
|
||||
}
|
||||
Err(err) => {
|
||||
warn!(error = %err, "failed to revoke agent credentials");
|
||||
Err(json_error(
|
||||
Err(ApiError::new(
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
"revoke_agent_failed",
|
||||
&err.to_string(),
|
||||
@@ -335,7 +335,7 @@ pub async fn set_agent_nickname(
|
||||
State(state): State<AppState>,
|
||||
AxumPath(agent_id): AxumPath<String>,
|
||||
Json(req): Json<SetAgentNicknameRequest>,
|
||||
) -> Result<impl IntoResponse, (StatusCode, Json<serde_json::Value>)> {
|
||||
) -> Result<impl IntoResponse, ApiError> {
|
||||
let normalized = req
|
||||
.nickname
|
||||
.as_deref()
|
||||
@@ -389,7 +389,7 @@ pub async fn set_agent_nickname(
|
||||
}
|
||||
Err(err) => {
|
||||
warn!(error = %err, "failed to update agent nickname");
|
||||
Err(json_error(
|
||||
Err(ApiError::new(
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
"set_agent_nickname_failed",
|
||||
&err.to_string(),
|
||||
|
||||
@@ -7,8 +7,8 @@ use axum::response::IntoResponse;
|
||||
use tracing::warn;
|
||||
use wakey_agent::protocol::{AgentCommand, InventoryRequest, WakeRequest};
|
||||
|
||||
use crate::api::ApiError;
|
||||
use crate::api::commands::relay_agent_command;
|
||||
use crate::api::json_error;
|
||||
use crate::runtime::AppState;
|
||||
|
||||
mod build;
|
||||
@@ -29,12 +29,12 @@ use types::{
|
||||
pub async fn list_fleet_devices(
|
||||
State(state): State<AppState>,
|
||||
Query(query): Query<ListFleetDevicesQuery>,
|
||||
) -> Result<impl IntoResponse, (StatusCode, Json<serde_json::Value>)> {
|
||||
) -> Result<impl IntoResponse, ApiError> {
|
||||
match load_fleet_devices(&state, &query).await {
|
||||
Ok(devices) => Ok((StatusCode::OK, Json(devices))),
|
||||
Err(err) => {
|
||||
warn!(error = %err, "failed to list fleet devices");
|
||||
Err(json_error(
|
||||
Err(ApiError::new(
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
"list_fleet_devices_failed",
|
||||
&err.to_string(),
|
||||
@@ -46,7 +46,7 @@ pub async fn list_fleet_devices(
|
||||
pub async fn refresh_fleet_devices(
|
||||
State(state): State<AppState>,
|
||||
Json(req): Json<RefreshFleetDevicesRequest>,
|
||||
) -> Result<impl IntoResponse, (StatusCode, Json<serde_json::Value>)> {
|
||||
) -> Result<impl IntoResponse, ApiError> {
|
||||
let mut agent_ids = if req.agent_ids.is_empty() {
|
||||
let sessions = state.sessions.read().await;
|
||||
sessions.keys().cloned().collect::<Vec<_>>()
|
||||
@@ -131,11 +131,11 @@ pub async fn refresh_fleet_devices(
|
||||
.map(|error| error.message)
|
||||
.or_else(|| Some("inventory command failed".into())),
|
||||
}),
|
||||
Err((status, body)) => results.push(RefreshFleetAgentResult {
|
||||
Err(err) => results.push(RefreshFleetAgentResult {
|
||||
agent_id,
|
||||
status: "error".into(),
|
||||
accepted: 0,
|
||||
error: Some(format!("{status}: {}", body.0)),
|
||||
error: Some(format!("{}: {}", err.status, err.message)),
|
||||
}),
|
||||
}
|
||||
}
|
||||
@@ -152,7 +152,7 @@ pub async fn refresh_fleet_devices(
|
||||
pub async fn wake_fleet_device(
|
||||
State(state): State<AppState>,
|
||||
Json(req): Json<WakeFleetDeviceRequest>,
|
||||
) -> Result<impl IntoResponse, (StatusCode, Json<serde_json::Value>)> {
|
||||
) -> Result<impl IntoResponse, ApiError> {
|
||||
let query = ListFleetDevicesQuery {
|
||||
query: None,
|
||||
presence: None,
|
||||
@@ -163,7 +163,7 @@ pub async fn wake_fleet_device(
|
||||
};
|
||||
let devices = load_fleet_devices(&state, &query).await.map_err(|err| {
|
||||
warn!(error = %err, "failed loading fleet devices for wake");
|
||||
json_error(
|
||||
ApiError::new(
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
"load_fleet_devices_failed",
|
||||
&err.to_string(),
|
||||
@@ -174,7 +174,7 @@ pub async fn wake_fleet_device(
|
||||
.into_iter()
|
||||
.find(|device| device.device_key == req.device_key)
|
||||
.ok_or_else(|| {
|
||||
json_error(
|
||||
ApiError::new(
|
||||
StatusCode::NOT_FOUND,
|
||||
"fleet_device_not_found",
|
||||
"fleet device not found",
|
||||
@@ -189,7 +189,7 @@ pub async fn wake_fleet_device(
|
||||
None => device.recommended_route,
|
||||
}
|
||||
.ok_or_else(|| {
|
||||
json_error(
|
||||
ApiError::new(
|
||||
StatusCode::BAD_REQUEST,
|
||||
"wake_route_unavailable",
|
||||
"no wakeable connected MAC-backed route is available",
|
||||
@@ -197,21 +197,21 @@ pub async fn wake_fleet_device(
|
||||
})?;
|
||||
|
||||
let Some(mac) = route.mac else {
|
||||
return Err(json_error(
|
||||
return Err(ApiError::new(
|
||||
StatusCode::BAD_REQUEST,
|
||||
"wake_route_unavailable",
|
||||
"selected route does not include a MAC address",
|
||||
));
|
||||
};
|
||||
if !route.connected {
|
||||
return Err(json_error(
|
||||
return Err(ApiError::new(
|
||||
StatusCode::BAD_REQUEST,
|
||||
"wake_route_unavailable",
|
||||
"selected route agent is not connected",
|
||||
));
|
||||
}
|
||||
if !route.wakeable {
|
||||
return Err(json_error(
|
||||
return Err(ApiError::new(
|
||||
StatusCode::BAD_REQUEST,
|
||||
"wake_route_unavailable",
|
||||
"selected route is not wakeable",
|
||||
|
||||
@@ -5,7 +5,7 @@ use axum::response::IntoResponse;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tracing::warn;
|
||||
|
||||
use crate::api::json_error;
|
||||
use crate::api::ApiError;
|
||||
use crate::runtime::AppState;
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
@@ -17,9 +17,7 @@ pub struct StateStatsResponse {
|
||||
pub expired_enroll_token_count: usize,
|
||||
}
|
||||
|
||||
pub async fn state_stats(
|
||||
State(state): State<AppState>,
|
||||
) -> Result<impl IntoResponse, (StatusCode, Json<serde_json::Value>)> {
|
||||
pub async fn state_stats(State(state): State<AppState>) -> Result<impl IntoResponse, ApiError> {
|
||||
match state.store.stats().await {
|
||||
Ok(stats) => Ok((
|
||||
StatusCode::OK,
|
||||
@@ -33,7 +31,7 @@ pub async fn state_stats(
|
||||
)),
|
||||
Err(err) => {
|
||||
warn!(error = %err, "failed to read state stats");
|
||||
Err(json_error(
|
||||
Err(ApiError::new(
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
"state_stats_failed",
|
||||
&err.to_string(),
|
||||
|
||||
@@ -17,18 +17,55 @@ pub use control::{
|
||||
revoke_agent, revoke_enroll_token, set_agent_nickname, state_stats, wake_fleet_device,
|
||||
};
|
||||
|
||||
pub fn json_error(
|
||||
status: StatusCode,
|
||||
code: &str,
|
||||
message: &str,
|
||||
) -> (StatusCode, Json<serde_json::Value>) {
|
||||
(
|
||||
status,
|
||||
Json(serde_json::json!({
|
||||
"error": {
|
||||
"code": code,
|
||||
"message": message,
|
||||
}
|
||||
})),
|
||||
)
|
||||
use axum::response::{IntoResponse, Response};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct ApiErrorResponse {
|
||||
pub error: ApiErrorDetail,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct ApiErrorDetail {
|
||||
pub code: String,
|
||||
pub message: String,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub details: Option<serde_json::Value>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ApiError {
|
||||
pub status: StatusCode,
|
||||
pub code: String,
|
||||
pub message: String,
|
||||
pub details: Option<serde_json::Value>,
|
||||
}
|
||||
|
||||
impl ApiError {
|
||||
pub fn new(status: StatusCode, code: impl Into<String>, message: impl Into<String>) -> Self {
|
||||
Self {
|
||||
status,
|
||||
code: code.into(),
|
||||
message: message.into(),
|
||||
details: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_details(mut self, details: serde_json::Value) -> Self {
|
||||
self.details = Some(details);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoResponse for ApiError {
|
||||
fn into_response(self) -> Response {
|
||||
let body = Json(ApiErrorResponse {
|
||||
error: ApiErrorDetail {
|
||||
code: self.code,
|
||||
message: self.message,
|
||||
details: self.details,
|
||||
},
|
||||
});
|
||||
(self.status, body).into_response()
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user