Co-authored-by: aider (openai/andrew/openrouter/deepseek/deepseek-chat-v3.1) <aider@aider.chat>
135 lines
4.2 KiB
Rust
135 lines
4.2 KiB
Rust
use anyhow::Result;
|
|
use axum::{
|
|
Router,
|
|
routing::post,
|
|
};
|
|
use clap::Command;
|
|
use log::{debug, info};
|
|
use std::net::SocketAddr;
|
|
use std::path::PathBuf;
|
|
use std::sync::Arc;
|
|
use tokio::sync::Mutex;
|
|
use tower_http::cors::CorsLayer;
|
|
use tower::ServiceBuilder;
|
|
use tower_http::trace::TraceLayer;
|
|
use crate::config;
|
|
use crate::services::item_service::ItemService;
|
|
|
|
pub mod common;
|
|
mod api;
|
|
mod pages;
|
|
mod mcp;
|
|
|
|
pub use common::{AppState, logging_middleware, create_auth_middleware};
|
|
|
|
pub fn mode_server(
|
|
cmd: &mut Command,
|
|
settings: &config::Settings,
|
|
conn: &mut rusqlite::Connection,
|
|
data_path: PathBuf,
|
|
) -> Result<()> {
|
|
// Get server address from args or config with default
|
|
let server_address = if let Some(addr) = &settings.server_address() {
|
|
addr.clone()
|
|
} else if let Some(server_config) = &settings.server {
|
|
server_config.address.clone().unwrap_or_else(|| "127.0.0.1".to_string())
|
|
} else {
|
|
"127.0.0.1".to_string()
|
|
};
|
|
|
|
// Get server port from args or config with default
|
|
let server_port = if let Some(port) = settings.server_port() {
|
|
port
|
|
} else if let Some(server_config) = &settings.server {
|
|
server_config.port.unwrap_or(21080)
|
|
} else {
|
|
21080
|
|
};
|
|
|
|
let server_config = common::ServerConfig {
|
|
address: server_address,
|
|
port: Some(server_port),
|
|
password: settings.server_password(),
|
|
password_hash: settings.server_password_hash(),
|
|
};
|
|
|
|
// Create ItemService once
|
|
let item_service = ItemService::new(data_path.clone());
|
|
|
|
// We need to move the connection into the async runtime
|
|
let rt = tokio::runtime::Runtime::new()?;
|
|
// Take ownership of the connection and move it into the async runtime
|
|
let owned_conn = std::mem::replace(conn, rusqlite::Connection::open_in_memory()?);
|
|
let cmd = cmd.clone();
|
|
let settings = settings.clone();
|
|
rt.block_on(run_server(server_config, owned_conn, data_path, item_service, cmd, settings))
|
|
}
|
|
|
|
async fn run_server(
|
|
config: common::ServerConfig,
|
|
conn: rusqlite::Connection,
|
|
data_dir: PathBuf,
|
|
item_service: ItemService,
|
|
_cmd: Command,
|
|
settings: config::Settings,
|
|
) -> Result<()> {
|
|
// Construct address with port
|
|
let bind_address = if let Some(port) = config.port {
|
|
format!("{}:{}", config.address, port)
|
|
} else {
|
|
format!("{}:21080", config.address)
|
|
};
|
|
|
|
debug!("SERVER: Starting REST HTTP server on {}", bind_address);
|
|
|
|
// Use the existing database connection
|
|
let db_conn = Arc::new(Mutex::new(conn));
|
|
|
|
let state = AppState {
|
|
db: db_conn,
|
|
data_dir: data_dir.clone(),
|
|
item_service: Arc::new(item_service),
|
|
cmd: Arc::new(Mutex::new(Command::new("keep"))),
|
|
settings: Arc::new(settings.clone()),
|
|
};
|
|
|
|
// Create MCP router
|
|
let mcp_router = Router::new()
|
|
.route("/mcp", post(mcp::handle_mcp_request))
|
|
.with_state(state.clone());
|
|
|
|
// Create the app with documentation routes open and others protected
|
|
let app = Router::new()
|
|
// Add documentation routes without authentication
|
|
.merge(api::add_docs_routes(Router::new()))
|
|
// Add API, pages, and MCP routes with authentication
|
|
.merge(
|
|
Router::new()
|
|
.merge(api::add_routes(Router::new()))
|
|
.merge(pages::add_routes(Router::new()))
|
|
.merge(mcp_router)
|
|
.layer(axum::middleware::from_fn(create_auth_middleware(config.password.clone(), config.password_hash.clone())))
|
|
)
|
|
// Apply state to all routes
|
|
.with_state(state)
|
|
// Add other middleware layers to all routes
|
|
.layer(axum::middleware::from_fn(logging_middleware))
|
|
.layer(
|
|
ServiceBuilder::new()
|
|
.layer(TraceLayer::new_for_http())
|
|
.layer(CorsLayer::permissive())
|
|
);
|
|
|
|
let addr: SocketAddr = bind_address.parse()?;
|
|
|
|
info!("SERVER: HTTP server listening on {}", addr);
|
|
|
|
let listener = tokio::net::TcpListener::bind(addr).await?;
|
|
axum::serve(
|
|
listener,
|
|
app.into_make_service_with_connect_info::<SocketAddr>()
|
|
).await?;
|
|
|
|
Ok(())
|
|
}
|