Skip to main content

fukurow_api/
handlers.rs

1//! API request handlers
2
3use axum::{
4    extract::{State, Json},
5    http::StatusCode,
6    response::Json as JsonResponse,
7};
8use std::sync::Arc;
9use tokio::sync::RwLock;
10use std::time::Instant;
11
12use crate::models::*;
13use fukurow_engine::ReasonerEngine;
14use fukurow_domain_cyber::threat_intelligence::ThreatProcessor;
15
16/// Shared application state
17#[derive(Clone)]
18pub struct AppState {
19    pub reasoner: Arc<ReasonerEngine>,
20    pub threat_processor: Arc<RwLock<ThreatProcessor>>,
21    pub start_time: Instant,
22}
23
24/// Health check handler
25pub async fn health_check(State(state): State<AppState>) -> JsonResponse<ApiResponse<HealthResponse>> {
26    let uptime = state.start_time.elapsed();
27
28    let response = HealthResponse {
29        status: "healthy".to_string(),
30        version: env!("CARGO_PKG_VERSION").to_string(),
31        uptime_seconds: uptime.as_secs(),
32    };
33
34    JsonResponse(ApiResponse::success(response))
35}
36
37/// Submit cyber event handler
38pub async fn submit_event(
39    State(state): State<AppState>,
40    Json(request): Json<SubmitEventRequest>,
41) -> Result<JsonResponse<ApiResponse<String>>, (StatusCode, JsonResponse<ApiResponse<String>>)> {
42    match state.reasoner.add_event(request.event).await {
43        Ok(_) => {
44            let response = ApiResponse::success("Event submitted successfully".to_string());
45            Ok(JsonResponse(response))
46        }
47        Err(e) => {
48            let error_response = ApiResponse::error(format!("Failed to submit event: {}", e));
49            Err((StatusCode::INTERNAL_SERVER_ERROR, JsonResponse(error_response)))
50        }
51    }
52}
53
54/// Execute reasoning handler
55pub async fn execute_reasoning(
56    State(state): State<AppState>,
57    Json(_request): Json<ReasoningRequest>,
58) -> Result<JsonResponse<ApiResponse<ReasoningResponse>>, (StatusCode, JsonResponse<ApiResponse<String>>)> {
59    let start = Instant::now();
60
61    match state.reasoner.reason().await {
62        Ok(actions) => {
63            let execution_time = start.elapsed();
64
65            let response = ReasoningResponse {
66                actions,
67                execution_time_ms: execution_time.as_millis() as u64,
68                event_count: 0, // TODO: Get actual event count from reasoner
69            };
70
71            Ok(JsonResponse(ApiResponse::success(response)))
72        }
73        Err(e) => {
74            let error_response = ApiResponse::error(format!("Reasoning failed: {}", e));
75            Err((StatusCode::INTERNAL_SERVER_ERROR, JsonResponse(error_response)))
76        }
77    }
78}
79
80/// Query graph handler
81pub async fn query_graph(
82    State(state): State<AppState>,
83    Json(request): Json<GraphQueryRequest>,
84) -> Result<JsonResponse<ApiResponse<GraphQueryResponse>>, (StatusCode, JsonResponse<ApiResponse<String>>)> {
85    let store = state.reasoner.get_graph_store().await;
86    let graph_store = store.read().await;
87
88    let triples = graph_store.find_triples(
89        request.subject.as_deref(),
90        request.predicate.as_deref(),
91        request.object.as_deref(),
92    );
93
94    let count = triples.len();
95    let response = GraphQueryResponse {
96        triples: triples.into_iter().map(|stored| stored.triple.clone()).collect(),
97        count,
98    };
99
100    Ok(JsonResponse(ApiResponse::success(response)))
101}
102
103/// Get statistics handler
104pub async fn get_stats(State(state): State<AppState>) -> JsonResponse<ApiResponse<StatsResponse>> {
105    let uptime = state.start_time.elapsed();
106
107    // TODO: Get actual statistics from reasoner
108    let response = StatsResponse {
109        total_events: 0,
110        total_actions: 0,
111        uptime_seconds: uptime.as_secs(),
112        memory_usage_mb: None, // TODO: Implement memory usage tracking
113    };
114
115    JsonResponse(ApiResponse::success(response))
116}
117
118/// Reset reasoner state handler
119pub async fn reset_reasoner(
120    State(_state): State<AppState>,
121) -> Result<JsonResponse<ApiResponse<String>>, (StatusCode, JsonResponse<ApiResponse<String>>)> {
122    // TODO: Implement reset functionality - requires mutable access to reasoner
123    let error_response = ApiResponse::error("Reset functionality not yet implemented".to_string());
124    Err((StatusCode::NOT_IMPLEMENTED, JsonResponse(error_response)))
125}
126
127/// Add custom rule handler
128pub async fn add_rule(
129    State(_state): State<AppState>,
130    Json(_request): Json<AddRuleRequest>,
131) -> Result<JsonResponse<ApiResponse<String>>, (StatusCode, JsonResponse<ApiResponse<String>>)> {
132    // Note: This would require mutable access to reasoner, which needs design consideration
133    // For now, return not implemented
134    let error_response = ApiResponse::error("Adding custom rules not yet implemented".to_string());
135    Err((StatusCode::NOT_IMPLEMENTED, JsonResponse(error_response)))
136}
137
138/// Get threat intelligence info handler
139pub async fn get_threat_intel(
140    State(state): State<AppState>,
141) -> JsonResponse<ApiResponse<ThreatIntelResponse>> {
142    let threat_processor = state.threat_processor.read().await;
143    let statistics = threat_processor.get_statistics();
144
145    let response = ThreatIntelResponse {
146        indicators_count: statistics.get("total_indicators").copied().unwrap_or(0),
147        sources_count: statistics.get("sources").copied().unwrap_or(0),
148        last_updated: chrono::Utc::now().timestamp(),
149        statistics,
150    };
151
152    JsonResponse(ApiResponse::success(response))
153}
154
155/// Export threat indicators handler
156pub async fn export_threat_indicators(
157    State(state): State<AppState>,
158) -> Result<JsonResponse<ApiResponse<String>>, (StatusCode, JsonResponse<ApiResponse<String>>)> {
159    let threat_processor = state.threat_processor.read().await;
160
161    match threat_processor.export_indicators() {
162        Ok(json_data) => {
163            Ok(JsonResponse(ApiResponse::success(json_data)))
164        }
165        Err(e) => {
166            let error_response = ApiResponse::error(format!("Failed to export indicators: {}", e));
167            Err((StatusCode::INTERNAL_SERVER_ERROR, JsonResponse(error_response)))
168        }
169    }
170}
171
172/// Import threat indicators handler
173pub async fn import_threat_indicators(
174    State(state): State<AppState>,
175    Json(json_data): Json<String>,
176) -> Result<JsonResponse<ApiResponse<String>>, (StatusCode, JsonResponse<ApiResponse<String>>)> {
177    let mut threat_processor = state.threat_processor.write().await;
178
179    match threat_processor.import_indicators(&json_data) {
180        Ok(_) => {
181            let response = ApiResponse::success("Threat indicators imported successfully".to_string());
182            Ok(JsonResponse(response))
183        }
184        Err(e) => {
185            let error_response = ApiResponse::error(format!("Failed to import indicators: {}", e));
186            Err((StatusCode::INTERNAL_SERVER_ERROR, JsonResponse(error_response)))
187        }
188    }
189}