235 lines
5.6 KiB
Rust
235 lines
5.6 KiB
Rust
use std::collections::HashMap;
|
|
use std::future::Future;
|
|
use std::pin::Pin;
|
|
use std::sync::Mutex;
|
|
|
|
use anyhow::{bail, format_err, Error};
|
|
use http::request::Parts;
|
|
use http::HeaderMap;
|
|
use hyper::{Body, Method, Response};
|
|
use lazy_static::lazy_static;
|
|
|
|
use proxmox_router::{
|
|
list_subdirs_api_method, Router, RpcEnvironmentType, SubdirMap, UserInformation,
|
|
};
|
|
use proxmox_schema::api;
|
|
|
|
use proxmox_rest_server::{ApiConfig, AuthError, RestEnvironment, RestServer, ServerAdapter};
|
|
|
|
// Create a Dummy User information system
|
|
struct DummyUserInfo;
|
|
|
|
impl UserInformation for DummyUserInfo {
|
|
fn is_superuser(&self, _userid: &str) -> bool {
|
|
// Always return true here, so we have access to everthing
|
|
true
|
|
}
|
|
fn is_group_member(&self, _userid: &str, group: &str) -> bool {
|
|
group == "Group"
|
|
}
|
|
fn lookup_privs(&self, _userid: &str, _path: &[&str]) -> u64 {
|
|
u64::MAX
|
|
}
|
|
}
|
|
|
|
struct MinimalServer;
|
|
|
|
// implement the server adapter
|
|
impl ServerAdapter for MinimalServer {
|
|
// normally this would check and authenticate the user
|
|
fn check_auth(
|
|
&self,
|
|
_headers: &HeaderMap,
|
|
_method: &Method,
|
|
) -> Pin<
|
|
Box<
|
|
dyn Future<Output = Result<(String, Box<dyn UserInformation + Sync + Send>), AuthError>>
|
|
+ Send,
|
|
>,
|
|
> {
|
|
Box::pin(async move {
|
|
// get some global/cached userinfo
|
|
let userinfo: Box<dyn UserInformation + Sync + Send> = Box::new(DummyUserInfo);
|
|
// Do some user checks, e.g. cookie/csrf
|
|
Ok(("User".to_string(), userinfo))
|
|
})
|
|
}
|
|
|
|
// this should return the index page of the webserver
|
|
// iow. what the user browses to
|
|
fn get_index(
|
|
&self,
|
|
_env: RestEnvironment,
|
|
_parts: Parts,
|
|
) -> Pin<Box<dyn Future<Output = Response<Body>> + Send>> {
|
|
Box::pin(async move {
|
|
// build an index page
|
|
http::Response::builder()
|
|
.body("hello world".into())
|
|
.unwrap()
|
|
})
|
|
}
|
|
}
|
|
|
|
// a few examples on how to do api calls with the Router
|
|
|
|
#[api]
|
|
/// A simple ping method. returns "pong"
|
|
fn ping() -> Result<String, Error> {
|
|
Ok("pong".to_string())
|
|
}
|
|
|
|
lazy_static! {
|
|
static ref ITEM_MAP: Mutex<HashMap<String, String>> = Mutex::new(HashMap::new());
|
|
}
|
|
|
|
#[api]
|
|
/// Lists all current items
|
|
fn list_items() -> Result<Vec<String>, Error> {
|
|
Ok(ITEM_MAP.lock().unwrap().keys().cloned().collect())
|
|
}
|
|
|
|
#[api(
|
|
input: {
|
|
properties: {
|
|
name: {
|
|
type: String,
|
|
description: "The name",
|
|
},
|
|
value: {
|
|
type: String,
|
|
description: "The value",
|
|
},
|
|
},
|
|
},
|
|
)]
|
|
/// creates a new item
|
|
fn create_item(name: String, value: String) -> Result<(), Error> {
|
|
let mut map = ITEM_MAP.lock().unwrap();
|
|
if map.contains_key(&name) {
|
|
bail!("{} already exists", name);
|
|
}
|
|
|
|
map.insert(name, value);
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[api(
|
|
input: {
|
|
properties: {
|
|
name: {
|
|
type: String,
|
|
description: "The name",
|
|
},
|
|
},
|
|
},
|
|
)]
|
|
/// returns the value of an item
|
|
fn get_item(name: String) -> Result<String, Error> {
|
|
ITEM_MAP
|
|
.lock()
|
|
.unwrap()
|
|
.get(&name)
|
|
.map(|s| s.to_string())
|
|
.ok_or_else(|| format_err!("no such item '{}'", name))
|
|
}
|
|
|
|
#[api(
|
|
input: {
|
|
properties: {
|
|
name: {
|
|
type: String,
|
|
description: "The name",
|
|
},
|
|
value: {
|
|
type: String,
|
|
description: "The value",
|
|
},
|
|
},
|
|
},
|
|
)]
|
|
/// updates an item
|
|
fn update_item(name: String, value: String) -> Result<(), Error> {
|
|
if let Some(val) = ITEM_MAP.lock().unwrap().get_mut(&name) {
|
|
*val = value;
|
|
} else {
|
|
bail!("no such item '{}'", name);
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
#[api(
|
|
input: {
|
|
properties: {
|
|
name: {
|
|
type: String,
|
|
description: "The name",
|
|
},
|
|
},
|
|
},
|
|
)]
|
|
/// deletes an item
|
|
fn delete_item(name: String) -> Result<(), Error> {
|
|
if ITEM_MAP.lock().unwrap().remove(&name).is_none() {
|
|
bail!("no such item '{}'", name);
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
const ITEM_ROUTER: Router = Router::new()
|
|
.get(&API_METHOD_GET_ITEM)
|
|
.put(&API_METHOD_UPDATE_ITEM)
|
|
.delete(&API_METHOD_DELETE_ITEM);
|
|
|
|
const SUBDIRS: SubdirMap = &[
|
|
(
|
|
"items",
|
|
&Router::new()
|
|
.get(&API_METHOD_LIST_ITEMS)
|
|
.post(&API_METHOD_CREATE_ITEM)
|
|
.match_all("name", &ITEM_ROUTER),
|
|
),
|
|
("ping", &Router::new().get(&API_METHOD_PING)),
|
|
];
|
|
|
|
const ROUTER: Router = Router::new()
|
|
.get(&list_subdirs_api_method!(SUBDIRS))
|
|
.subdirs(SUBDIRS);
|
|
|
|
async fn run() -> Result<(), Error> {
|
|
// we first have to configure the api environment (basedir etc.)
|
|
|
|
let config = ApiConfig::new(
|
|
"/var/tmp/",
|
|
&ROUTER,
|
|
RpcEnvironmentType::PUBLIC,
|
|
MinimalServer,
|
|
)?;
|
|
let rest_server = RestServer::new(config);
|
|
|
|
// then we have to create a daemon that listens, accepts and serves
|
|
// the api to clients
|
|
proxmox_rest_server::daemon::create_daemon(
|
|
([127, 0, 0, 1], 65000).into(),
|
|
move |listener| {
|
|
let incoming = hyper::server::conn::AddrIncoming::from_listener(listener)?;
|
|
|
|
Ok(async move {
|
|
hyper::Server::builder(incoming).serve(rest_server).await?;
|
|
|
|
Ok(())
|
|
})
|
|
},
|
|
None,
|
|
)
|
|
.await?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn main() -> Result<(), Error> {
|
|
let rt = tokio::runtime::Runtime::new()?;
|
|
rt.block_on(async { run().await })
|
|
}
|