mirror of
https://github.com/speatzle/nfsense.git
synced 2025-05-10 18:38:22 +00:00
Get JsonRPC Working
This commit is contained in:
parent
0a603b6642
commit
42cc14cb14
6 changed files with 160 additions and 66 deletions
48
src/api/mod.rs
Normal file
48
src/api/mod.rs
Normal file
|
@ -0,0 +1,48 @@
|
|||
mod network;
|
||||
mod system;
|
||||
|
||||
use crate::state::RpcState;
|
||||
use jsonrpsee::{
|
||||
types::{error::ErrorCode, ErrorObject},
|
||||
RpcModule,
|
||||
};
|
||||
|
||||
use custom_error::custom_error;
|
||||
use tracing::info;
|
||||
|
||||
custom_error! { pub ApiError
|
||||
InvalidParams = "Invalid Parameters",
|
||||
Leet = "1337",
|
||||
}
|
||||
|
||||
impl Into<ErrorObject<'static>> for ApiError {
|
||||
fn into(self) -> ErrorObject<'static> {
|
||||
match self {
|
||||
Self::InvalidParams => ErrorCode::InvalidParams,
|
||||
Self::Leet => ErrorCode::ServerError(1337),
|
||||
_ => ErrorCode::InternalError,
|
||||
}
|
||||
.into()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_rpc_module(state: RpcState) -> RpcModule<RpcState> {
|
||||
let mut module = RpcModule::new(state);
|
||||
|
||||
module
|
||||
.register_method("ping", |_, _| {
|
||||
info!("ping called");
|
||||
"pong"
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
module
|
||||
.register_method("System.GetUsers", system::get_users)
|
||||
.unwrap();
|
||||
|
||||
module
|
||||
.register_method("Network.GetStaticRoutes", network::get_static_routes)
|
||||
.unwrap();
|
||||
|
||||
module
|
||||
}
|
13
src/api/network.rs
Normal file
13
src/api/network.rs
Normal file
|
@ -0,0 +1,13 @@
|
|||
use jsonrpsee::types::Params;
|
||||
|
||||
use crate::{definitions::network::StaticRoute, state::RpcState};
|
||||
|
||||
use super::ApiError;
|
||||
|
||||
pub fn get_static_routes(_: Params, state: &RpcState) -> Result<Vec<StaticRoute>, ApiError> {
|
||||
Ok(state
|
||||
.config_manager
|
||||
.get_pending_config()
|
||||
.network
|
||||
.static_routes)
|
||||
}
|
10
src/api/system.rs
Normal file
10
src/api/system.rs
Normal file
|
@ -0,0 +1,10 @@
|
|||
use std::collections::HashMap;
|
||||
|
||||
use crate::{definitions::system::User, state::RpcState};
|
||||
use jsonrpsee::types::Params;
|
||||
|
||||
use super::ApiError;
|
||||
|
||||
pub fn get_users(_: Params, state: &RpcState) -> Result<HashMap<String, User>, ApiError> {
|
||||
Ok(state.config_manager.get_pending_config().system.users)
|
||||
}
|
15
src/main.rs
15
src/main.rs
|
@ -5,6 +5,7 @@ use std::{
|
|||
sync::{Arc, RwLock},
|
||||
};
|
||||
|
||||
use crate::state::RpcState;
|
||||
use axum::{middleware, Router};
|
||||
use config_manager::ConfigManager;
|
||||
use state::AppState;
|
||||
|
@ -15,6 +16,7 @@ use tracing::info;
|
|||
use tracing_subscriber;
|
||||
use web::auth::SessionState;
|
||||
|
||||
mod api;
|
||||
mod config_manager;
|
||||
mod definitions;
|
||||
mod state;
|
||||
|
@ -41,12 +43,17 @@ async fn main() {
|
|||
|
||||
// TODO Check Config Manager Setup Error
|
||||
let config_manager = ConfigManager::new().unwrap();
|
||||
let session_state = SessionState {
|
||||
sessions: Arc::new(RwLock::new(HashMap::new())),
|
||||
};
|
||||
|
||||
let app_state = AppState {
|
||||
config_manager,
|
||||
session_state: SessionState {
|
||||
sessions: Arc::new(RwLock::new(HashMap::new())),
|
||||
},
|
||||
config_manager: config_manager.clone(),
|
||||
session_state: session_state.clone(),
|
||||
rpc_module: api::new_rpc_module(RpcState {
|
||||
config_manager,
|
||||
session_state,
|
||||
}),
|
||||
};
|
||||
|
||||
// Note: The Router Works Bottom Up, So the auth middleware will only applies to everything above it.
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
use jsonrpsee::RpcModule;
|
||||
|
||||
use super::config_manager::ConfigManager;
|
||||
use super::web::auth::SessionState;
|
||||
|
||||
|
@ -5,4 +7,11 @@ use super::web::auth::SessionState;
|
|||
pub struct AppState {
|
||||
pub config_manager: ConfigManager,
|
||||
pub session_state: SessionState,
|
||||
pub rpc_module: RpcModule<RpcState>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct RpcState {
|
||||
pub config_manager: ConfigManager,
|
||||
pub session_state: SessionState,
|
||||
}
|
||||
|
|
131
src/web/rpc.rs
131
src/web/rpc.rs
|
@ -1,42 +1,60 @@
|
|||
use std::collections::HashMap;
|
||||
|
||||
use super::super::definitions::network::StaticRoute;
|
||||
use super::super::definitions::system::User;
|
||||
use super::super::AppState;
|
||||
use crate::AppState;
|
||||
use axum::routing::post;
|
||||
use axum::{Json, Router};
|
||||
use jsonrpsee::types::Params;
|
||||
use tower_cookies::{Cookie, Cookies};
|
||||
use jsonrpsee::core::traits::ToRpcParams;
|
||||
use jsonrpsee::core::Error;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::value::RawValue;
|
||||
|
||||
use axum::{
|
||||
extract::Extension,
|
||||
extract::State,
|
||||
http::{Request, StatusCode},
|
||||
middleware::{self, Next},
|
||||
response::{IntoResponse, Response},
|
||||
};
|
||||
|
||||
use jsonrpsee::server::{RpcModule, Server};
|
||||
use axum::{extract::Extension, extract::State, response::IntoResponse};
|
||||
|
||||
use tracing::info;
|
||||
|
||||
use custom_error::custom_error;
|
||||
|
||||
custom_error! { ApiError
|
||||
BadRequest = "Bad Request Parameters",
|
||||
// TODO fix this "workaround"
|
||||
struct ParamConverter {
|
||||
params: Option<Box<RawValue>>,
|
||||
}
|
||||
|
||||
struct RpcRequest<'a> {
|
||||
impl ToRpcParams for ParamConverter {
|
||||
fn to_rpc_params(self) -> Result<Option<Box<RawValue>>, Error> {
|
||||
let s = String::from_utf8(serde_json::to_vec(&self.params)?);
|
||||
match s {
|
||||
Ok(s) => {
|
||||
return RawValue::from_string(s)
|
||||
.map(Some)
|
||||
.map_err(Error::ParseError)
|
||||
}
|
||||
// TODO make this a Parse error wrapping Utf8Error
|
||||
Err(err) => return Err(Error::AlreadyStopped),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct RpcRequest {
|
||||
id: i64,
|
||||
params: Params<'a>,
|
||||
params: Option<Box<RawValue>>,
|
||||
jsonrpc: String,
|
||||
method: String,
|
||||
}
|
||||
|
||||
struct RpcResponse<'a> {
|
||||
#[derive(Clone, Deserialize, Serialize)]
|
||||
struct RpcResponse {
|
||||
id: i64,
|
||||
payload: Params<'a>,
|
||||
jsonrpc: String,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
result: Option<Box<RawValue>>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
error: Option<RpcErrorObject>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Deserialize, Serialize)]
|
||||
|
||||
struct RpcErrorObject {
|
||||
code: i64,
|
||||
message: String,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
data: Option<Box<RawValue>>,
|
||||
}
|
||||
|
||||
pub fn routes() -> Router<super::super::AppState> {
|
||||
|
@ -46,49 +64,38 @@ pub fn routes() -> Router<super::super::AppState> {
|
|||
async fn api_handler(
|
||||
State(state): State<AppState>,
|
||||
session: Extension<super::auth::Session>,
|
||||
Json(rpc_request): Json<RpcRequest<'_>>,
|
||||
body: String,
|
||||
) -> impl IntoResponse {
|
||||
info!("api hit! user: {:?}", session.username);
|
||||
let module = RpcModule::new(state);
|
||||
module
|
||||
.register_method("say_hello", |_, _| {
|
||||
println!("say_hello method called!");
|
||||
"Hello there!!"
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
module
|
||||
.register_method("System.GetUsers", get_users)
|
||||
.unwrap();
|
||||
module
|
||||
.register_method("Network.GetStaticRoutes", get_static_routes)
|
||||
.unwrap();
|
||||
// TODO handle Parse Error
|
||||
let req: RpcRequest = serde_json::from_str(&body).unwrap();
|
||||
|
||||
let res = module.call(&rpc_request.method, rpc_request.params).await;
|
||||
// TODO check version
|
||||
|
||||
let params = ParamConverter { params: req.params };
|
||||
|
||||
// TODO check Permissions for method here?
|
||||
|
||||
let res: Result<Option<Box<RawValue>>, Error> =
|
||||
state.rpc_module.call(&req.method, params).await;
|
||||
|
||||
match res {
|
||||
Ok(res) => RpcResponse {
|
||||
id: rpc_request.id,
|
||||
jsonrpc: rpc_request.jsonrpc,
|
||||
payload: res,
|
||||
},
|
||||
// TODO make Error Response
|
||||
Err(err) => RpcResponse {
|
||||
id: rpc_request.id,
|
||||
jsonrpc: rpc_request.jsonrpc,
|
||||
payload: res,
|
||||
},
|
||||
Ok(res) => Json(RpcResponse {
|
||||
id: req.id,
|
||||
jsonrpc: req.jsonrpc,
|
||||
result: res,
|
||||
error: None,
|
||||
}),
|
||||
Err(err) => Json(RpcResponse {
|
||||
id: req.id,
|
||||
jsonrpc: req.jsonrpc,
|
||||
result: None,
|
||||
error: Some(RpcErrorObject {
|
||||
code: 10,
|
||||
message: err.to_string(),
|
||||
data: None,
|
||||
}),
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_users(_: Params, state: &AppState) -> Result<HashMap<String, User>, String> {
|
||||
Ok(state.config_manager.get_pending_config().system.users)
|
||||
}
|
||||
|
||||
fn get_static_routes(_: Params, state: &AppState) -> Result<Vec<StaticRoute>, String> {
|
||||
Ok(state
|
||||
.config_manager
|
||||
.get_pending_config()
|
||||
.network
|
||||
.static_routes)
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue