67a69ab073
- Go backend API with full CRUD for all modules - Rust analytics service with parallel processing - C runtime with POSIX shared memory IPC - PostgreSQL schema with 30+ tables - Redis cache, Kafka event streaming - WebSocket hub, automation engine - PDF generation, Resend email integration - JWT auth, multi-tenant - Docker Compose deployment - Nginx reverse proxy Refs: BOC-001
168 lines
4.2 KiB
Rust
168 lines
4.2 KiB
Rust
use axum::{
|
|
routing::{get, post},
|
|
Router,
|
|
Json,
|
|
extract::State,
|
|
};
|
|
use serde::{Deserialize, Serialize};
|
|
use std::sync::Arc;
|
|
use tokio::sync::RwLock;
|
|
use tracing::{info, error};
|
|
|
|
mod analytics;
|
|
mod reports;
|
|
mod ipc;
|
|
|
|
use analytics::AnalyticsEngine;
|
|
use reports::ReportGenerator;
|
|
|
|
#[derive(Clone)]
|
|
struct AppState {
|
|
analytics: Arc<RwLock<AnalyticsEngine>>,
|
|
reports: Arc<RwLock<ReportGenerator>>,
|
|
}
|
|
|
|
#[derive(Serialize)]
|
|
struct HealthResponse {
|
|
status: String,
|
|
service: String,
|
|
version: String,
|
|
}
|
|
|
|
#[derive(Deserialize)]
|
|
struct ReportRequest {
|
|
tenant_id: String,
|
|
report_type: String,
|
|
parameters: serde_json::Value,
|
|
}
|
|
|
|
#[derive(Serialize)]
|
|
struct ReportResponse {
|
|
report_id: String,
|
|
status: String,
|
|
data: Option<serde_json::Value>,
|
|
}
|
|
|
|
#[derive(Deserialize)]
|
|
struct AnalyticsRequest {
|
|
tenant_id: String,
|
|
metric: String,
|
|
period: String,
|
|
}
|
|
|
|
#[derive(Serialize)]
|
|
struct AnalyticsResponse {
|
|
metric: String,
|
|
value: f64,
|
|
trend: f64,
|
|
breakdown: Vec<BreakdownItem>,
|
|
}
|
|
|
|
#[derive(Serialize)]
|
|
struct BreakdownItem {
|
|
label: String,
|
|
value: f64,
|
|
}
|
|
|
|
#[tokio::main]
|
|
async fn main() {
|
|
tracing_subscriber::fmt()
|
|
.with_env_filter("boc_rust_service=info")
|
|
.init();
|
|
|
|
info!("BOC Rust Service starting...");
|
|
|
|
let state = AppState {
|
|
analytics: Arc::new(RwLock::new(AnalyticsEngine::new())),
|
|
reports: Arc::new(RwLock::new(ReportGenerator::new())),
|
|
};
|
|
|
|
let app = Router::new()
|
|
.route("/health", get(health_handler))
|
|
.route("/api/v1/reports/generate", post(generate_report))
|
|
.route("/api/v1/analytics/query", post(query_analytics))
|
|
.route("/api/v1/analytics/batch", post(batch_analytics))
|
|
.with_state(state);
|
|
|
|
let listener = tokio::net::TcpListener::bind("0.0.0.0:9093")
|
|
.await
|
|
.expect("Failed to bind port 9093");
|
|
|
|
info!("BOC Rust Service listening on 0.0.0.0:9093");
|
|
|
|
axum::serve(listener, app)
|
|
.await
|
|
.expect("Server failed");
|
|
}
|
|
|
|
async fn health_handler() -> Json<HealthResponse> {
|
|
Json(HealthResponse {
|
|
status: "ok".to_string(),
|
|
service: "boc-rust-service".to_string(),
|
|
version: env!("CARGO_PKG_VERSION").to_string(),
|
|
})
|
|
}
|
|
|
|
async fn generate_report(
|
|
State(state): State<AppState>,
|
|
Json(req): Json<ReportRequest>,
|
|
) -> Json<ReportResponse> {
|
|
info!("Generating report: {} for tenant: {}", req.report_type, req.tenant_id);
|
|
|
|
let reports = state.reports.read().await;
|
|
match reports.generate(&req.tenant_id, &req.report_type, &req.parameters).await {
|
|
Ok(data) => Json(ReportResponse {
|
|
report_id: uuid::Uuid::new_v4().to_string(),
|
|
status: "completed".to_string(),
|
|
data: Some(data),
|
|
}),
|
|
Err(e) => {
|
|
error!("Report generation failed: {}", e);
|
|
Json(ReportResponse {
|
|
report_id: uuid::Uuid::new_v4().to_string(),
|
|
status: "failed".to_string(),
|
|
data: None,
|
|
})
|
|
}
|
|
}
|
|
}
|
|
|
|
async fn query_analytics(
|
|
State(state): State<AppState>,
|
|
Json(req): Json<AnalyticsRequest>,
|
|
) -> Json<AnalyticsResponse> {
|
|
let analytics = state.analytics.read().await;
|
|
let result = analytics.query(&req.tenant_id, &req.metric, &req.period).await;
|
|
|
|
Json(AnalyticsResponse {
|
|
metric: req.metric,
|
|
value: result.value,
|
|
trend: result.trend,
|
|
breakdown: result.breakdown.into_iter()
|
|
.map(|(label, value)| BreakdownItem { label, value })
|
|
.collect(),
|
|
})
|
|
}
|
|
|
|
async fn batch_analytics(
|
|
State(state): State<AppState>,
|
|
Json(reqs): Json<Vec<AnalyticsRequest>>,
|
|
) -> Json<Vec<AnalyticsResponse>> {
|
|
let analytics = state.analytics.read().await;
|
|
|
|
let mut responses = Vec::with_capacity(reqs.len());
|
|
for req in reqs {
|
|
let result = analytics.query(&req.tenant_id, &req.metric, &req.period).await;
|
|
responses.push(AnalyticsResponse {
|
|
metric: req.metric.clone(),
|
|
value: result.value,
|
|
trend: result.trend,
|
|
breakdown: result.breakdown.into_iter()
|
|
.map(|(label, value)| BreakdownItem { label, value })
|
|
.collect(),
|
|
});
|
|
}
|
|
|
|
Json(responses)
|
|
}
|