slave 0.4.0

Laravel-inspired CLI for Rust MCP servers: scaffold apps whose business operations are MCP tools over Streamable HTTP, with built-in auth, users, roles and Turso migrations
//! Assembles the HTTP application.
//!
//! The application exposes exactly two endpoints:
//!
//! - `/mcp`: the MCP Streamable HTTP endpoint, the only interface for business
//!   operations. Domain modules add tools to it, never HTTP routes.
//! - `GET /health`: liveness/readiness probe for load balancers and
//!   orchestrators, which cannot speak MCP. It exposes no business data.

use axum::extract::State;
use axum::http::StatusCode;
use axum::routing::get;
use axum::{Json, Router};
use serde::Serialize;

use crate::base::mcp;
use crate::base::state::AppState;

pub fn router(state: AppState) -> Router {
    Router::new()
        .route("/health", get(health))
        .nest_service(mcp::PATH, mcp::service(state.clone()))
        .with_state(state)
}

#[derive(Serialize)]
struct Health {
    status: &'static str,
    app: String,
    database: &'static str,
}

/// Reports whether the app is up and its database reachable (503 if not).
async fn health(State(state): State<AppState>) -> (StatusCode, Json<Health>) {
    // `query`, not `execute`: Turso rejects `execute` for statements that return rows.
    let database_ok = match state.database.connect().await {
        Ok(conn) => conn.query("SELECT 1", ()).await.is_ok(),
        Err(_) => false,
    };
    let (status, database) = if database_ok {
        (StatusCode::OK, "ok")
    } else {
        (StatusCode::SERVICE_UNAVAILABLE, "unavailable")
    };
    let body = Health {
        status: if database_ok { "ok" } else { "degraded" },
        app: state.config.name.clone(),
        database,
    };
    (status, Json(body))
}