diff --git a/backend/Cargo.lock b/backend/Cargo.lock index 80323f7..11a1466 100644 --- a/backend/Cargo.lock +++ b/backend/Cargo.lock @@ -807,9 +807,12 @@ dependencies = [ "futures 0.3.19", "jsonrpc-core", "jsonrpc-pubsub", + "jsonrpc-server-utils", "log", + "parity-tokio-ipc", "serde", "serde_json", + "tokio", "url", ] diff --git a/backend/Cargo.toml b/backend/Cargo.toml index 1a85e52..5058485 100644 --- a/backend/Cargo.toml +++ b/backend/Cargo.toml @@ -10,9 +10,12 @@ figment = "^0.10" jsonrpc-core = "~18.0" jsonrpc-derive = "~18.0" jsonrpc-ipc-server = "~18.0" -jsonrpc-core-client = "~18.0" serde = "^1.0" +[dependencies.jsonrpc-core-client] +version = "~18.0" +features = ["ipc"] + [dependencies.procfs] version = "^0.12" features = ["chrono"] diff --git a/backend/src/daemon/rpc.rs b/backend/src/daemon/rpc.rs index 1023071..0bae1ec 100644 --- a/backend/src/daemon/rpc.rs +++ b/backend/src/daemon/rpc.rs @@ -1,4 +1,5 @@ -use crate::rpc::{Status, WeywotRpc}; +use crate::models::status::DaemonStatus; +use crate::rpc::WeywotRpc; use chrono::Local; use jsonrpc_core::Result as JsonRpcResult; use procfs::process::Process; @@ -9,8 +10,8 @@ use std::process; pub struct RpcDaemon; impl WeywotRpc for RpcDaemon { - fn status(&self) -> JsonRpcResult { - Ok(Status { + fn status(&self) -> JsonRpcResult { + Ok(DaemonStatus { version: env!("CARGO_PKG_VERSION").into(), runtime: proc_runtime().unwrap_or(0), }) diff --git a/backend/src/main.rs b/backend/src/main.rs index e008ab6..cf0a755 100644 --- a/backend/src/main.rs +++ b/backend/src/main.rs @@ -1,4 +1,5 @@ mod daemon; +mod models; mod rpc; mod server; diff --git a/backend/src/models/mod.rs b/backend/src/models/mod.rs new file mode 100644 index 0000000..16a1865 --- /dev/null +++ b/backend/src/models/mod.rs @@ -0,0 +1 @@ +pub mod status; \ No newline at end of file diff --git a/backend/src/models/status.rs b/backend/src/models/status.rs new file mode 100644 index 0000000..3d1e94d --- /dev/null +++ b/backend/src/models/status.rs @@ -0,0 +1,18 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Deserialize, Serialize)] +pub struct DaemonStatus { + pub version: String, + pub runtime: i64, +} + +#[derive(Debug, Serialize)] +pub struct WebServerStatus { + pub version: String, +} + +#[derive(Debug, Serialize)] +pub struct StatusResponse { + pub daemon: DaemonStatus, + pub web: WebServerStatus, +} diff --git a/backend/src/rpc.rs b/backend/src/rpc.rs index 627e984..bf1309e 100644 --- a/backend/src/rpc.rs +++ b/backend/src/rpc.rs @@ -1,15 +1,9 @@ +use crate::models::status::DaemonStatus; use jsonrpc_core::Result; use jsonrpc_derive::rpc; -use serde::{Deserialize, Serialize}; - -#[derive(Debug, Deserialize, Serialize)] -pub struct Status { - pub version: String, - pub runtime: i64, -} #[rpc] pub trait WeywotRpc { #[rpc(name = "status")] - fn status(&self) -> Result; + fn status(&self) -> Result; } diff --git a/backend/src/server/config.rs b/backend/src/server/config.rs index 57854ca..d9f601e 100644 --- a/backend/src/server/config.rs +++ b/backend/src/server/config.rs @@ -4,6 +4,7 @@ use serde::{Deserialize, Serialize}; pub struct Config { pub address: String, pub port: u16, + pub socket: String } impl Default for Config { @@ -11,6 +12,7 @@ impl Default for Config { Config { address: "::".into(), port: 8998, + socket: "weywot.sock".into(), } } } diff --git a/backend/src/server/context.rs b/backend/src/server/context.rs new file mode 100644 index 0000000..8015a55 --- /dev/null +++ b/backend/src/server/context.rs @@ -0,0 +1,31 @@ +use crate::rpc::gen_client::Client; +use crate::server::error::{ApiError, ErrorResponse}; +use jsonrpc_core_client::transports::ipc::connect; +use rocket::http::Status as HttpStatus; +use rocket::serde::json::Json; +use std::path::PathBuf; + +pub struct Context { + socket_path: PathBuf, +} + +impl Context { + pub fn new>(socket: P) -> Self { + Self { + socket_path: socket.into(), + } + } + + pub async fn client(&self) -> Result { + match connect(&self.socket_path).await { + Ok(client) => Ok(client), + Err(e) => Err(( + HttpStatus::ServiceUnavailable, + Json(ErrorResponse::new(format!( + "Cannot connect to daemon: {}", + e + ))), + )), + } + } +} diff --git a/backend/src/server/error.rs b/backend/src/server/error.rs new file mode 100644 index 0000000..d974d45 --- /dev/null +++ b/backend/src/server/error.rs @@ -0,0 +1,18 @@ +use rocket::http::Status as HttpStatus; +use rocket::serde::json::Json; +use rocket::serde::Serialize; + +#[derive(Serialize)] +pub struct ErrorResponse { + pub error: String, +} + +pub type ApiError = (HttpStatus, Json); + +impl ErrorResponse { + pub fn new>(message: S) -> Self { + Self { + error: message.into(), + } + } +} diff --git a/backend/src/server/mod.rs b/backend/src/server/mod.rs index e4aa81b..d947b72 100644 --- a/backend/src/server/mod.rs +++ b/backend/src/server/mod.rs @@ -1,8 +1,11 @@ mod config; +mod context; +mod error; mod routes; use config::Config; use argh::FromArgs; +use context::Context; use rocket; use rocket::fairing::AdHoc; use rocket::figment::providers::{Env, Format, Serialized, Toml}; @@ -24,6 +27,10 @@ struct Arguments { /// configuration file #[argh(option, short = 'c')] config: Option, + + /// weywot daemon socket path + #[argh(option, short = 's')] + socket: Option, } #[rocket::main] @@ -42,10 +49,22 @@ pub async fn main() -> Result<(), rocket::Error> { if let Some(port) = args.port { figment = figment.merge(("port", port)); } + if let Some(socket) = args.socket { + figment = figment.merge(("socket", socket)); + } + + let context = Context::new( + figment + .find_value("socket") + .expect("No daemon socket path configured") + .as_str() + .expect("Invalid daemon socket path"), + ); rocket::custom(figment) - .mount("/", rocket::routes![routes::hello::hello]) + .mount("/", rocket::routes![routes::status::get_status]) .attach(AdHoc::config::()) + .manage(context) .ignite() .await? .launch() diff --git a/backend/src/server/routes/hello.rs b/backend/src/server/routes/hello.rs deleted file mode 100644 index ca8411c..0000000 --- a/backend/src/server/routes/hello.rs +++ /dev/null @@ -1,6 +0,0 @@ -use rocket; - -#[rocket::get("/")] -pub async fn hello() -> &'static str { - "Hello, world!" -} diff --git a/backend/src/server/routes/mod.rs b/backend/src/server/routes/mod.rs index 152adf1..822c729 100644 --- a/backend/src/server/routes/mod.rs +++ b/backend/src/server/routes/mod.rs @@ -1 +1 @@ -pub mod hello; +pub mod status; diff --git a/backend/src/server/routes/status.rs b/backend/src/server/routes/status.rs index e69de29..4ef6e7f 100644 --- a/backend/src/server/routes/status.rs +++ b/backend/src/server/routes/status.rs @@ -0,0 +1,18 @@ +use crate::models::status::{StatusResponse, WebServerStatus}; +use crate::server::context::Context; +use crate::server::error::ApiError; +use rocket::serde::json::Json; +use rocket::State; + +#[rocket::get("/status")] +pub async fn get_status( + state: &State, +) -> Result, ApiError> { + let client = state.client().await?; + Ok(Json(StatusResponse { + daemon: client.status().await.unwrap(), + web: WebServerStatus { + version: env!("CARGO_PKG_VERSION").into(), + }, + })) +}