use std::sync::Arc;
use axum::routing::{get, post};
use axum::Router;
use parking_lot::RwLock;
use tower_http::cors::CorsLayer;
use graph_engine::GraphEngine;
use query_router::QueryRouter;
use relational_engine::RelationalEngine;
use tensor_blob::BlobStore;
use tensor_cache::Cache;
use tensor_checkpoint::CheckpointManager;
use tensor_store::TensorStore;
use tensor_unified::UnifiedEngine;
use tensor_vault::Vault;
use vector_engine::VectorEngine;
use crate::config::AuthConfig;
use crate::metrics::ServerMetrics;
mod assets;
pub mod handlers;
pub mod icons;
pub mod templates;
pub use assets::ADMIN_CSS;
pub struct ChainStatus {
pub raft_state: String,
pub current_term: u64,
pub commit_index: u64,
pub log_length: usize,
pub leader_id: Option<String>,
pub fast_path_rate: f32,
pub heartbeat_success_rate: f32,
pub heartbeat_successes: u64,
pub heartbeat_failures: u64,
pub quorum_checks: u64,
pub quorum_lost_events: u64,
pub leader_step_downs: u64,
pub tx_started: u64,
pub tx_committed: u64,
pub tx_aborted: u64,
pub tx_timed_out: u64,
pub tx_conflicts: u64,
pub tx_commit_rate: f32,
pub tx_conflict_rate: f32,
pub tx_pending: usize,
pub deadlocks_detected: u64,
pub victims_aborted: u64,
pub detection_cycles: u64,
pub max_cycle_length: u64,
pub deadlock_enabled: bool,
}
pub struct AdminContext {
pub relational: Arc<RelationalEngine>,
pub vector: Arc<VectorEngine>,
pub graph: Arc<GraphEngine>,
pub unified: Option<Arc<UnifiedEngine>>,
pub vault: Option<Arc<Vault>>,
pub cache: Option<Arc<Cache>>,
pub blob: Option<Arc<tokio::sync::Mutex<BlobStore>>>,
pub checkpoint: Option<Arc<CheckpointManager>>,
pub store: Option<TensorStore>,
pub chain: Option<Arc<ChainStatus>>,
pub auth_config: Option<AuthConfig>,
pub metrics: Option<Arc<ServerMetrics>>,
pub query_router: Option<Arc<RwLock<QueryRouter>>>,
}
impl AdminContext {
#[must_use]
pub const fn new(
relational: Arc<RelationalEngine>,
vector: Arc<VectorEngine>,
graph: Arc<GraphEngine>,
) -> Self {
Self {
relational,
vector,
graph,
unified: None,
vault: None,
cache: None,
blob: None,
checkpoint: None,
store: None,
chain: None,
auth_config: None,
metrics: None,
query_router: None,
}
}
#[must_use]
pub fn with_auth(mut self, config: Option<AuthConfig>) -> Self {
self.auth_config = config;
self
}
#[must_use]
pub fn with_metrics(mut self, metrics: Option<Arc<ServerMetrics>>) -> Self {
self.metrics = metrics;
self
}
#[must_use]
pub fn with_unified(mut self, unified: Option<Arc<UnifiedEngine>>) -> Self {
self.unified = unified;
self
}
#[must_use]
pub fn with_vault(mut self, vault: Option<Arc<Vault>>) -> Self {
self.vault = vault;
self
}
#[must_use]
pub fn with_cache(mut self, cache: Option<Arc<Cache>>) -> Self {
self.cache = cache;
self
}
#[must_use]
pub fn with_blob(mut self, blob: Option<Arc<tokio::sync::Mutex<BlobStore>>>) -> Self {
self.blob = blob;
self
}
#[must_use]
pub fn with_checkpoint(mut self, checkpoint: Option<Arc<CheckpointManager>>) -> Self {
self.checkpoint = checkpoint;
self
}
#[must_use]
pub fn with_store(mut self, store: Option<TensorStore>) -> Self {
self.store = store;
self
}
#[must_use]
pub fn with_chain(mut self, chain: Option<Arc<ChainStatus>>) -> Self {
self.chain = chain;
self
}
#[must_use]
pub fn with_query_router(mut self, router: Option<Arc<RwLock<QueryRouter>>>) -> Self {
self.query_router = router;
self
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum NavItem {
Dashboard,
Relational,
Vector,
Graph,
Contraction,
Vault,
Cache,
Blob,
Checkpoint,
Storage,
Chain,
}
fn vector_routes() -> Router<Arc<AdminContext>> {
Router::new()
.route("/vector", get(handlers::vector::collections_list))
.route(
"/vector/_default",
get(handlers::vector::default_collection_detail),
)
.route(
"/vector/_default/points",
get(handlers::vector::default_points_list),
)
.route(
"/vector/_default/points/{point_id}",
get(handlers::vector::default_point_detail),
)
.route(
"/vector/_default/search",
get(handlers::vector::default_search_form)
.post(handlers::vector::default_search_submit),
)
.route(
"/vector/{collection}",
get(handlers::vector::collection_detail),
)
.route(
"/vector/{collection}/points",
get(handlers::vector::points_list),
)
.route(
"/vector/{collection}/points/{point_id}",
get(handlers::vector::point_detail),
)
.route(
"/vector/{collection}/search",
get(handlers::vector::search_form).post(handlers::vector::search_submit),
)
}
fn graph_routes() -> Router<Arc<AdminContext>> {
Router::new()
.route("/graph", get(handlers::graph::overview))
.route("/graph/nodes", get(handlers::graph::nodes_list))
.route("/graph/edges", get(handlers::graph::edges_list))
.route(
"/graph/path",
get(handlers::graph::path_finder).post(handlers::graph::path_finder_submit),
)
.route(
"/graph/algorithms",
get(handlers::graph::algorithms).post(handlers::graph::algorithms_submit),
)
.route(
"/graph/algorithms/dashboard",
get(handlers::graph_algorithms::dashboard),
)
.route(
"/graph/algorithms/execute",
get(handlers::graph_algorithms::execute_form)
.post(handlers::graph_algorithms::execute_submit),
)
}
fn storage_routes() -> Router<Arc<AdminContext>> {
Router::new()
.route("/blob", get(handlers::blob::overview))
.route("/blob/artifacts", get(handlers::blob::artifacts_list))
.route(
"/blob/artifacts/{artifact_id}",
get(handlers::blob::artifact_detail),
)
.route("/checkpoint", get(handlers::checkpoint::list_view))
.route("/checkpoint/config", get(handlers::checkpoint::config_view))
.route("/checkpoint/{id}", get(handlers::checkpoint::detail_view))
.route("/storage", get(handlers::storage::overview))
.route("/storage/shards", get(handlers::storage::shard_heatmap))
.route("/storage/wal", get(handlers::storage::wal_status))
.route("/cache", get(handlers::cache::stats_dashboard))
.route("/cache/config", get(handlers::cache::config_viewer))
.route("/cache/layers", get(handlers::cache::layers_breakdown))
}
pub fn router(ctx: Arc<AdminContext>) -> Router {
let cors = CorsLayer::new()
.allow_origin([axum::http::HeaderValue::from_static(
"http://localhost:5173",
)])
.allow_methods([
axum::http::Method::GET,
axum::http::Method::POST,
axum::http::Method::OPTIONS,
])
.allow_headers([axum::http::header::CONTENT_TYPE]);
Router::new()
.route("/", get(handlers::dashboard))
.route("/relational", get(handlers::relational::tables_list))
.route(
"/relational/{table}",
get(handlers::relational::table_detail),
)
.route(
"/relational/{table}/rows",
get(handlers::relational::table_rows),
)
.merge(vector_routes())
.merge(graph_routes())
.route("/contraction", get(handlers::contraction::explainability))
.route("/contraction/ranking", get(handlers::contraction::ranking))
.route(
"/contraction/sensitivity",
get(handlers::contraction::sensitivity),
)
.route(
"/contraction/counterfactual",
get(handlers::contraction::counterfactual),
)
.route("/vault", get(handlers::vault::secrets_list))
.route("/vault/audit", get(handlers::vault::audit_log))
.route("/vault/status", get(handlers::vault::vault_status))
.route("/vault/security", get(handlers::vault::security_dashboard))
.route(
"/vault/blast-radius",
get(handlers::vault::blast_radius_view),
)
.route(
"/vault/critical",
get(handlers::vault::critical_entities_view),
)
.route("/vault/reveal", post(handlers::vault::reveal_value))
.route("/vault/{*key}", get(handlers::vault::secret_detail))
.merge(storage_routes())
.route("/chain", get(handlers::chain::consensus))
.route("/chain/transactions", get(handlers::chain::transactions))
.route("/chain/deadlocks", get(handlers::chain::deadlocks))
.route("/metrics", get(handlers::metrics::dashboard))
.route("/api/metrics", get(handlers::metrics::api_snapshot))
.route("/api/graph/subgraph", get(handlers::graph::api_subgraph))
.route("/api/query", axum::routing::post(handlers::api_query))
.route("/api/galaxy", post(handlers::api_galaxy))
.route("/api/execute", post(handlers::api_execute))
.layer(cors)
.with_state(ctx)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_admin_context_new() {
let relational = Arc::new(RelationalEngine::new());
let vector = Arc::new(VectorEngine::new());
let graph = Arc::new(GraphEngine::new());
let ctx = AdminContext::new(relational, vector, graph);
assert!(ctx.auth_config.is_none());
assert!(ctx.metrics.is_none());
assert!(ctx.unified.is_none());
assert!(ctx.vault.is_none());
assert!(ctx.cache.is_none());
assert!(ctx.blob.is_none());
assert!(ctx.checkpoint.is_none());
assert!(ctx.store.is_none());
assert!(ctx.chain.is_none());
}
#[test]
fn test_admin_context_with_auth() {
use crate::config::ApiKey;
let relational = Arc::new(RelationalEngine::new());
let vector = Arc::new(VectorEngine::new());
let graph = Arc::new(GraphEngine::new());
let auth_config = AuthConfig::new().with_api_key(ApiKey::new(
"test-api-key-12345678".to_string(),
"user:test".to_string(),
));
let ctx = AdminContext::new(relational, vector, graph).with_auth(Some(auth_config));
assert!(ctx.auth_config.is_some());
}
#[test]
fn test_admin_context_with_unified() {
let relational = Arc::new(RelationalEngine::new());
let vector = Arc::new(VectorEngine::new());
let graph = Arc::new(GraphEngine::new());
let unified = Arc::new(UnifiedEngine::new());
let ctx = AdminContext::new(relational, vector, graph).with_unified(Some(unified));
assert!(ctx.unified.is_some());
}
#[test]
fn test_admin_context_with_vault() {
let relational = Arc::new(RelationalEngine::new());
let vector = Arc::new(VectorEngine::new());
let graph = Arc::new(GraphEngine::new());
let ctx = AdminContext::new(relational, vector, graph).with_vault(None);
assert!(ctx.vault.is_none());
}
#[test]
fn test_admin_context_with_cache() {
let relational = Arc::new(RelationalEngine::new());
let vector = Arc::new(VectorEngine::new());
let graph = Arc::new(GraphEngine::new());
let cache = Arc::new(Cache::new());
let ctx = AdminContext::new(relational, vector, graph).with_cache(Some(cache));
assert!(ctx.cache.is_some());
}
#[test]
fn test_router_creation() {
let relational = Arc::new(RelationalEngine::new());
let vector = Arc::new(VectorEngine::new());
let graph = Arc::new(GraphEngine::new());
let ctx = Arc::new(AdminContext::new(relational, vector, graph));
let _router = router(ctx);
}
}