ninox-server 0.1.0

HTTP/WebSocket server exposing the Ninox engine to the native app and web dashboard.
Documentation
use ninox_core::{BrainEntry, BrainIndex, QueryFilters};
use axum::{
    extract::{Path, Query, State},
    http::StatusCode,
    routing::{get, post},
    Json, Router,
};
use serde::{Deserialize, Serialize};
use std::sync::Arc;

pub fn brain_router(brain: Arc<BrainIndex>) -> Router {
    Router::new()
        .route("/index", post(rebuild_index))
        .route("/query", get(query_entries))
        .route("/entry/*path", get(get_entry))
        .with_state(brain)
}

#[derive(Serialize)]
struct IndexResponse {
    count: usize,
}

async fn rebuild_index(
    State(brain): State<Arc<BrainIndex>>,
) -> Result<Json<IndexResponse>, StatusCode> {
    match brain.rebuild() {
        Ok(count) => Ok(Json(IndexResponse { count })),
        Err(err) => {
            tracing::error!("brain rebuild: {err}");
            Err(StatusCode::INTERNAL_SERVER_ERROR)
        }
    }
}

#[derive(Deserialize)]
struct QueryParams {
    q: Option<String>,
    #[serde(rename = "type")]
    entry_type: Option<String>,
    tag: Option<String>,
}

async fn query_entries(
    State(brain): State<Arc<BrainIndex>>,
    Query(params): Query<QueryParams>,
) -> Result<Json<Vec<BrainEntry>>, StatusCode> {
    let text = params.q.unwrap_or_default();
    let filters = QueryFilters {
        entry_type: params.entry_type,
        tag: params.tag,
    };
    match brain.query(&text, filters) {
        Ok(entries) => Ok(Json(entries)),
        Err(err) => {
            tracing::error!("brain query: {err}");
            Err(StatusCode::INTERNAL_SERVER_ERROR)
        }
    }
}

async fn get_entry(
    State(brain): State<Arc<BrainIndex>>,
    Path(path): Path<String>,
) -> Result<Json<BrainEntry>, StatusCode> {
    match brain.get(&path) {
        Ok(Some(entry)) => Ok(Json(entry)),
        Ok(None) => Err(StatusCode::NOT_FOUND),
        Err(err) => {
            tracing::error!("brain get {path}: {err}");
            Err(StatusCode::INTERNAL_SERVER_ERROR)
        }
    }
}