Skip to main content

scematica_protocol/
server.rs

1use axum::{middleware, routing::get, Json, Router};
2use std::{net::SocketAddr, sync::Arc};
3use tracing::info;
4
5use crate::{
6    facilitator::Facilitator,
7    middleware::{payment_middleware, PaymentGate},
8    types::PaymentRequirements,
9};
10
11pub struct ProtocolServer {
12    gate: Arc<PaymentGate>,
13    addr: SocketAddr,
14}
15
16impl ProtocolServer {
17    pub fn new(
18        facilitator: Arc<Facilitator>,
19        requirements: Vec<PaymentRequirements>,
20        addr: SocketAddr,
21    ) -> Self {
22        let gate = Arc::new(PaymentGate {
23            facilitator,
24            requirements,
25        });
26        Self { gate, addr }
27    }
28
29    pub async fn run(self) -> anyhow::Result<()> {
30        let gate = self.gate.clone();
31
32        // Paid routes — each requires a valid X-Payment header
33        let paid = Router::new()
34            .route("/signals/pools", get(pools_handler))
35            .route("/signals/trades", get(trades_handler))
36            .route("/stats/nn", get(nn_stats_handler))
37            .route("/stats/metrics", get(metrics_handler))
38            .layer(middleware::from_fn_with_state(
39                gate.clone(),
40                payment_middleware,
41            ));
42
43        // Free routes
44        let accepts = gate.requirements.clone();
45        let free = Router::new().route("/health", get(health_handler)).route(
46            "/supported",
47            get(move || {
48                let a = accepts.clone();
49                async move { Json(serde_json::json!({ "accepts": a })) }
50            }),
51        );
52
53        let app = Router::new().merge(paid).merge(free);
54
55        info!("🌐 Scematica Protocol API on {}", self.addr);
56        axum::Server::bind(&self.addr)
57            .serve(app.into_make_service())
58            .await?;
59        Ok(())
60    }
61}
62
63// ── paid handlers ─────────────────────────────────────────────────────────────
64
65async fn pools_handler() -> Json<serde_json::Value> {
66    let data = read_json("scematica-filter-stats.json");
67    Json(serde_json::json!({
68        "pool_stats": data,
69        "timestamp": chrono::Utc::now(),
70    }))
71}
72
73async fn trades_handler() -> Json<serde_json::Value> {
74    let trades: Vec<serde_json::Value> = std::fs::read_to_string("scematica-trades.jsonl")
75        .unwrap_or_default()
76        .lines()
77        .filter_map(|l| serde_json::from_str(l).ok())
78        .collect::<Vec<_>>()
79        .into_iter()
80        .rev()
81        .take(200)
82        .collect();
83    let count = trades.len();
84    Json(serde_json::json!({ "trades": trades, "count": count, "timestamp": chrono::Utc::now() }))
85}
86
87async fn nn_stats_handler() -> Json<serde_json::Value> {
88    Json(serde_json::json!({
89        "nn_stats": read_json("scematica-nn-stats.json"),
90        "timestamp": chrono::Utc::now(),
91    }))
92}
93
94async fn metrics_handler() -> Json<serde_json::Value> {
95    Json(serde_json::json!({
96        "metrics": read_json("scematica-metrics.json"),
97        "strategy": read_json("scematica-strategy.json"),
98        "timestamp": chrono::Utc::now(),
99    }))
100}
101
102// ── free handlers ─────────────────────────────────────────────────────────────
103
104async fn health_handler() -> Json<serde_json::Value> {
105    Json(serde_json::json!({
106        "status": "ok",
107        "protocol": "scematica-x402",
108        "version": crate::types::X402_VERSION,
109    }))
110}
111
112// ── helpers ───────────────────────────────────────────────────────────────────
113
114fn read_json(path: &str) -> serde_json::Value {
115    std::fs::read_to_string(path)
116        .ok()
117        .and_then(|s| serde_json::from_str(&s).ok())
118        .unwrap_or(serde_json::Value::Null)
119}