CORS: - Restrict CORS to DASHBOARD_URL environment variable - Default to production dashboard domain Authentication: - Add AuthUser requirement to all agent management endpoints - Add AuthUser requirement to all command endpoints - Add AuthUser requirement to all metrics endpoints - Add audit logging for command execution (user_id tracked) Agent Security: - Replace Unicode characters with ASCII markers [OK]/[ERROR]/[WARNING] - Add certificate pinning for update downloads (allowlist domains) - Fix insecure temp file creation (use /var/run/gururmm with 0700 perms) - Fix rollback script backgrounding (use setsid instead of literal &) Dashboard Security: - Move token storage from localStorage to sessionStorage - Add proper TypeScript types (remove 'any' from error handlers) - Centralize token management functions Legacy Agent: - Add -AllowInsecureTLS parameter (opt-in required) - Add Windows Event Log audit trail when insecure mode used - Update documentation with security warnings Closes: Phase 1 items in issue #1 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
71 lines
2.0 KiB
Rust
71 lines
2.0 KiB
Rust
//! Metrics API endpoints
|
|
|
|
use axum::{
|
|
extract::{Path, Query, State},
|
|
http::StatusCode,
|
|
Json,
|
|
};
|
|
use chrono::{DateTime, Utc};
|
|
use serde::Deserialize;
|
|
use uuid::Uuid;
|
|
|
|
use crate::auth::AuthUser;
|
|
use crate::db::{self, Metrics, MetricsSummary};
|
|
use crate::AppState;
|
|
|
|
/// Query parameters for metrics
|
|
#[derive(Debug, Deserialize)]
|
|
pub struct MetricsQuery {
|
|
/// Number of records to return (default: 100)
|
|
pub limit: Option<i64>,
|
|
|
|
/// Start time for range query
|
|
pub start: Option<DateTime<Utc>>,
|
|
|
|
/// End time for range query
|
|
pub end: Option<DateTime<Utc>>,
|
|
}
|
|
|
|
/// Get metrics for a specific agent
|
|
/// Requires authentication.
|
|
pub async fn get_agent_metrics(
|
|
State(state): State<AppState>,
|
|
_user: AuthUser,
|
|
Path(id): Path<Uuid>,
|
|
Query(query): Query<MetricsQuery>,
|
|
) -> Result<Json<Vec<Metrics>>, (StatusCode, String)> {
|
|
// First verify the agent exists
|
|
let _agent = db::get_agent_by_id(&state.db, id)
|
|
.await
|
|
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?
|
|
.ok_or((StatusCode::NOT_FOUND, "Agent not found".to_string()))?;
|
|
|
|
let metrics = if let (Some(start), Some(end)) = (query.start, query.end) {
|
|
// Range query
|
|
db::get_agent_metrics_range(&state.db, id, start, end)
|
|
.await
|
|
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?
|
|
} else {
|
|
// Simple limit query
|
|
let limit = query.limit.unwrap_or(100).min(1000); // Cap at 1000
|
|
db::get_agent_metrics(&state.db, id, limit)
|
|
.await
|
|
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?
|
|
};
|
|
|
|
Ok(Json(metrics))
|
|
}
|
|
|
|
/// Get summary metrics across all agents
|
|
/// Requires authentication.
|
|
pub async fn get_summary(
|
|
State(state): State<AppState>,
|
|
_user: AuthUser,
|
|
) -> Result<Json<MetricsSummary>, (StatusCode, String)> {
|
|
let summary = db::get_metrics_summary(&state.db)
|
|
.await
|
|
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
|
|
|
|
Ok(Json(summary))
|
|
}
|