]> git.proxmox.com Git - proxmox-backup.git/blob - proxmox-rest-server/examples/minimal-rest-server.rs
update to first proxmox crate split
[proxmox-backup.git] / proxmox-rest-server / examples / minimal-rest-server.rs
1 use std::sync::Mutex;
2 use std::collections::HashMap;
3 use std::future::Future;
4 use std::pin::Pin;
5
6 use anyhow::{bail, format_err, Error};
7 use lazy_static::lazy_static;
8 use hyper::{Body, Response, Method};
9 use http::request::Parts;
10 use http::HeaderMap;
11
12 use proxmox_schema::api;
13 use proxmox_router::{list_subdirs_api_method, SubdirMap, Router, RpcEnvironmentType, UserInformation};
14
15 use proxmox_rest_server::{ServerAdapter, ApiConfig, AuthError, RestServer, RestEnvironment};
16
17 // Create a Dummy User information system
18 struct DummyUserInfo;
19
20 impl UserInformation for DummyUserInfo {
21 fn is_superuser(&self, _userid: &str) -> bool {
22 // Always return true here, so we have access to everthing
23 true
24 }
25 fn is_group_member(&self, _userid: &str, group: &str) -> bool {
26 group == "Group"
27 }
28 fn lookup_privs(&self, _userid: &str, _path: &[&str]) -> u64 {
29 u64::MAX
30 }
31 }
32
33 struct MinimalServer;
34
35 // implement the server adapter
36 impl ServerAdapter for MinimalServer {
37
38 // normally this would check and authenticate the user
39 fn check_auth(
40 &self,
41 _headers: &HeaderMap,
42 _method: &Method,
43 ) -> Pin<Box<dyn Future<Output = Result<(String, Box<dyn UserInformation + Sync + Send>), AuthError>> + Send>> {
44 Box::pin(async move {
45 // get some global/cached userinfo
46 let userinfo: Box<dyn UserInformation + Sync + Send> = Box::new(DummyUserInfo);
47 // Do some user checks, e.g. cookie/csrf
48 Ok(("User".to_string(), userinfo))
49 })
50 }
51
52 // this should return the index page of the webserver
53 // iow. what the user browses to
54 fn get_index(
55 &self,
56 _env: RestEnvironment,
57 _parts: Parts,
58 ) -> Pin<Box<dyn Future<Output = Response<Body>> + Send>> {
59 Box::pin(async move {
60 // build an index page
61 http::Response::builder()
62 .body("hello world".into())
63 .unwrap()
64 })
65 }
66 }
67
68 // a few examples on how to do api calls with the Router
69
70 #[api]
71 /// A simple ping method. returns "pong"
72 fn ping() -> Result<String, Error> {
73 Ok("pong".to_string())
74 }
75
76 lazy_static! {
77 static ref ITEM_MAP: Mutex<HashMap<String, String>> = Mutex::new(HashMap::new());
78 }
79
80 #[api]
81 /// Lists all current items
82 fn list_items() -> Result<Vec<String>, Error> {
83 Ok(ITEM_MAP.lock().unwrap().keys().map(|k| k.clone()).collect())
84 }
85
86 #[api(
87 input: {
88 properties: {
89 name: {
90 type: String,
91 description: "The name",
92 },
93 value: {
94 type: String,
95 description: "The value",
96 },
97 },
98 },
99 )]
100 /// creates a new item
101 fn create_item(name: String, value: String) -> Result<(), Error> {
102 let mut map = ITEM_MAP.lock().unwrap();
103 if map.contains_key(&name) {
104 bail!("{} already exists", name);
105 }
106
107 map.insert(name, value);
108
109 Ok(())
110 }
111
112 #[api(
113 input: {
114 properties: {
115 name: {
116 type: String,
117 description: "The name",
118 },
119 },
120 },
121 )]
122 /// returns the value of an item
123 fn get_item(name: String) -> Result<String, Error> {
124 ITEM_MAP.lock().unwrap().get(&name).map(|s| s.to_string()).ok_or_else(|| format_err!("no such item '{}'", name))
125 }
126
127 #[api(
128 input: {
129 properties: {
130 name: {
131 type: String,
132 description: "The name",
133 },
134 value: {
135 type: String,
136 description: "The value",
137 },
138 },
139 },
140 )]
141 /// updates an item
142 fn update_item(name: String, value: String) -> Result<(), Error> {
143 if let Some(val) = ITEM_MAP.lock().unwrap().get_mut(&name) {
144 *val = value;
145 } else {
146 bail!("no such item '{}'", name);
147 }
148 Ok(())
149 }
150
151 #[api(
152 input: {
153 properties: {
154 name: {
155 type: String,
156 description: "The name",
157 },
158 },
159 },
160 )]
161 /// deletes an item
162 fn delete_item(name: String) -> Result<(), Error> {
163 if ITEM_MAP.lock().unwrap().remove(&name).is_none() {
164 bail!("no such item '{}'", name);
165 }
166 Ok(())
167 }
168
169 const ITEM_ROUTER: Router = Router::new()
170 .get(&API_METHOD_GET_ITEM)
171 .put(&API_METHOD_UPDATE_ITEM)
172 .delete(&API_METHOD_DELETE_ITEM);
173
174 const SUBDIRS: SubdirMap = &[
175 (
176 "items",
177 &Router::new()
178 .get(&API_METHOD_LIST_ITEMS)
179 .post(&API_METHOD_CREATE_ITEM)
180 .match_all("name", &ITEM_ROUTER)
181 ),
182 (
183 "ping",
184 &Router::new()
185 .get(&API_METHOD_PING)
186 ),
187 ];
188
189 const ROUTER: Router = Router::new()
190 .get(&list_subdirs_api_method!(SUBDIRS))
191 .subdirs(SUBDIRS);
192
193 async fn run() -> Result<(), Error> {
194
195 // we first have to configure the api environment (basedir etc.)
196
197 let config = ApiConfig::new(
198 "/var/tmp/",
199 &ROUTER,
200 RpcEnvironmentType::PUBLIC,
201 MinimalServer,
202 )?;
203 let rest_server = RestServer::new(config);
204
205 // then we have to create a daemon that listens, accepts and serves
206 // the api to clients
207 proxmox_rest_server::daemon::create_daemon(
208 ([127, 0, 0, 1], 65000).into(),
209 move |listener| {
210 let incoming = hyper::server::conn::AddrIncoming::from_listener(listener)?;
211
212 Ok(async move {
213
214 hyper::Server::builder(incoming)
215 .serve(rest_server)
216 .await?;
217
218 Ok(())
219 })
220 },
221 ).await?;
222
223 Ok(())
224 }
225
226 fn main() -> Result<(), Error> {
227 let rt = tokio::runtime::Runtime::new()?;
228 rt.block_on(async { run().await })
229 }