Get JsonRPC Working

This commit is contained in:
Samuel Lorch 2023-10-28 15:44:12 +02:00
parent 0a603b6642
commit 42cc14cb14
6 changed files with 160 additions and 66 deletions

48
src/api/mod.rs Normal file
View 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
View 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
View 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)
}

View file

@ -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.

View file

@ -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,
}

View file

@ -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)
}