feat: Phase 2 data aggregation pipeline (Strike 4A)
Backend: - Stats ingestion consumer subscribing to corrosion.*.stats NATS subject - Hourly aggregation scheduler (runs :05 past every hour) - Daily cleanup job (03:00 UTC) with 7-day raw / 90-day hourly retention - Analytics API (summary, timeseries, CSV export) - Complete stats DB queries with aggregation and cleanup Frontend: - Analytics dashboard with ECharts integration - Player count and server performance charts - Time range selector (24h/7d/30d) - CSV export functionality - Real-time data loading Infrastructure: - Exposed NatsBridge.jetstream for consumer access - Background service initialization in main.rs Data flow: Plugin → NATS → Consumer → DB → Aggregation → API → Charts Unblocks Strike 4B (dashboards) and 4C (alerting). Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -65,6 +65,43 @@ async fn main() -> anyhow::Result<()> {
|
||||
// Bootstrap: create admin user + license on first run
|
||||
bootstrap_admin(&db).await;
|
||||
|
||||
// Initialize background services if NATS is available
|
||||
if let Some(ref nats_client) = nats {
|
||||
let nats_bridge = Arc::new(services::nats_bridge::NatsBridge::new(nats_client.clone()));
|
||||
|
||||
// Start stats consumer
|
||||
let stats_consumer = services::stats_consumer::StatsConsumerService::new(
|
||||
db.clone(),
|
||||
nats_bridge.clone(),
|
||||
);
|
||||
if let Err(e) = stats_consumer.start().await {
|
||||
tracing::error!("Failed to start stats consumer: {}", e);
|
||||
}
|
||||
|
||||
// Start scheduler service
|
||||
let scheduler = services::scheduler::SchedulerService::new(
|
||||
db.clone(),
|
||||
nats_bridge.clone(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
// Register stats jobs
|
||||
if let Err(e) = scheduler.register_stats_aggregation().await {
|
||||
tracing::error!("Failed to register stats aggregation job: {}", e);
|
||||
}
|
||||
if let Err(e) = scheduler.register_stats_cleanup().await {
|
||||
tracing::error!("Failed to register stats cleanup job: {}", e);
|
||||
}
|
||||
|
||||
if let Err(e) = scheduler.start().await {
|
||||
tracing::error!("Failed to start scheduler: {}", e);
|
||||
} else {
|
||||
tracing::info!("Scheduler service started");
|
||||
}
|
||||
} else {
|
||||
tracing::warn!("Skipping background services (NATS not available)");
|
||||
}
|
||||
|
||||
let state = Arc::new(AppState { db, nats, config });
|
||||
|
||||
// CORS — permissive in dev, locked down in production
|
||||
@@ -91,6 +128,7 @@ async fn main() -> anyhow::Result<()> {
|
||||
.nest("/api/early-access", api::early_access::router())
|
||||
.nest("/api/admin", api::admin::router())
|
||||
.nest("/api/ws", api::ws::router())
|
||||
.nest("/api/analytics", api::analytics::router())
|
||||
.layer(cors)
|
||||
.layer(TraceLayer::new_for_http())
|
||||
.with_state(state);
|
||||
|
||||
Reference in New Issue
Block a user