11use crate :: zinit:: { config, ZInit } ;
22use anyhow:: { bail, Context , Result } ;
33use axum:: {
4- routing :: { post , options } ,
5- http:: { StatusCode , HeaderMap } ,
4+ body :: Bytes ,
5+ http:: { HeaderMap , StatusCode } ,
66 response:: { IntoResponse , Response } ,
7+ routing:: { options, post} ,
78 Router ,
8- body:: Bytes ,
99} ;
1010use nix:: sys:: signal;
1111use serde:: { Deserialize , Serialize } ;
@@ -107,7 +107,7 @@ impl Api {
107107 http_port,
108108 }
109109 }
110-
110+
111111 // Serve HTTP proxy for JSON-RPC API
112112 async fn serve_http ( port : u16 , zinit : ZInit ) -> Result < ( ) > {
113113 // Handle JSON-RPC requests
@@ -116,38 +116,47 @@ impl Api {
116116 zinit : ZInit ,
117117 ) -> Result < Response , ( StatusCode , String ) > {
118118 // Process the JSON-RPC request
119- let request: JsonRpcRequest = serde_json:: from_slice ( & body)
120- . map_err ( |e| ( StatusCode :: BAD_REQUEST , format ! ( "Invalid JSON-RPC request: {}" , e) ) ) ?;
121-
119+ let request: JsonRpcRequest = serde_json:: from_slice ( & body) . map_err ( |e| {
120+ (
121+ StatusCode :: BAD_REQUEST ,
122+ format ! ( "Invalid JSON-RPC request: {}" , e) ,
123+ )
124+ } ) ?;
125+
122126 // We can't use Api::process_jsonrpc directly from an inner function
123127 // So we'll manually create a JsonRpcResponse
124128 let id = request. id . unwrap_or ( Value :: Null ) ;
125129 let method = request. method . clone ( ) ;
126130 let params = request. params . clone ( ) ;
127-
131+
128132 // Serialize the request parameters for the Unix socket
129133 let response_bytes = match method. as_str ( ) {
130134 "rpc.discover" => {
131135 // For discover, we can directly return the OpenRPC spec
132136 let spec: Value = match serde_json:: from_str ( OPENRPC_SPEC ) {
133137 Ok ( value) => value,
134138 Err ( err) => {
135- return Err ( ( StatusCode :: INTERNAL_SERVER_ERROR ,
136- format ! ( "Failed to parse OpenRPC spec: {}" , err) ) ) ;
139+ return Err ( (
140+ StatusCode :: INTERNAL_SERVER_ERROR ,
141+ format ! ( "Failed to parse OpenRPC spec: {}" , err) ,
142+ ) ) ;
137143 }
138144 } ;
139-
145+
140146 let response = JsonRpcResponse {
141147 jsonrpc : "2.0" . to_string ( ) ,
142148 id,
143149 result : Some ( spec) ,
144150 error : None ,
145151 } ;
146-
147- serde_json:: to_vec ( & response)
148- . map_err ( |e| ( StatusCode :: INTERNAL_SERVER_ERROR ,
149- format ! ( "Failed to serialize response: {}" , e) ) ) ?
150- } ,
152+
153+ serde_json:: to_vec ( & response) . map_err ( |e| {
154+ (
155+ StatusCode :: INTERNAL_SERVER_ERROR ,
156+ format ! ( "Failed to serialize response: {}" , e) ,
157+ )
158+ } ) ?
159+ }
151160 _ => {
152161 // For other methods, create a new JSON-RPC request to send to the Unix socket
153162 let unix_request = JsonRpcRequest {
@@ -156,47 +165,67 @@ impl Api {
156165 method,
157166 params,
158167 } ;
159-
168+
160169 // Serialize the request
161- let request_bytes = serde_json:: to_vec ( & unix_request)
162- . map_err ( |e| ( StatusCode :: INTERNAL_SERVER_ERROR ,
163- format ! ( "Failed to serialize request: {}" , e) ) ) ?;
164-
170+ let request_bytes = serde_json:: to_vec ( & unix_request) . map_err ( |e| {
171+ (
172+ StatusCode :: INTERNAL_SERVER_ERROR ,
173+ format ! ( "Failed to serialize request: {}" , e) ,
174+ )
175+ } ) ?;
176+
165177 // Create a Unix socket connection
166- let mut socket = UnixStream :: connect ( "/var/run/zinit.sock" )
167- . await
168- . map_err ( |e| ( StatusCode :: INTERNAL_SERVER_ERROR ,
169- format ! ( "Failed to connect to Zinit socket: {}" , e) ) ) ?;
170-
178+ let mut socket =
179+ UnixStream :: connect ( "/var/run/zinit.sock" )
180+ . await
181+ . map_err ( |e| {
182+ (
183+ StatusCode :: INTERNAL_SERVER_ERROR ,
184+ format ! ( "Failed to connect to Zinit socket: {}" , e) ,
185+ )
186+ } ) ?;
187+
171188 // Send the request
172- socket. write_all ( & request_bytes)
173- . await
174- . map_err ( |e| ( StatusCode :: INTERNAL_SERVER_ERROR ,
175- format ! ( "Failed to write to Zinit socket: {}" , e) ) ) ?;
176-
177- socket. write_all ( b"\n " )
178- . await
179- . map_err ( |e| ( StatusCode :: INTERNAL_SERVER_ERROR ,
180- format ! ( "Failed to write newline to Zinit socket: {}" , e) ) ) ?;
181-
189+ socket. write_all ( & request_bytes) . await . map_err ( |e| {
190+ (
191+ StatusCode :: INTERNAL_SERVER_ERROR ,
192+ format ! ( "Failed to write to Zinit socket: {}" , e) ,
193+ )
194+ } ) ?;
195+
196+ socket. write_all ( b"\n " ) . await . map_err ( |e| {
197+ (
198+ StatusCode :: INTERNAL_SERVER_ERROR ,
199+ format ! ( "Failed to write newline to Zinit socket: {}" , e) ,
200+ )
201+ } ) ?;
202+
182203 // Read the response
183204 let mut response_data = Vec :: new ( ) ;
184- socket. read_to_end ( & mut response_data)
185- . await
186- . map_err ( |e| ( StatusCode :: INTERNAL_SERVER_ERROR ,
187- format ! ( "Failed to read from Zinit socket: {}" , e) ) ) ?;
188-
205+ socket. read_to_end ( & mut response_data) . await . map_err ( |e| {
206+ (
207+ StatusCode :: INTERNAL_SERVER_ERROR ,
208+ format ! ( "Failed to read from Zinit socket: {}" , e) ,
209+ )
210+ } ) ?;
211+
189212 response_data
190213 }
191214 } ;
192-
215+
193216 // Create response with CORS headers
194217 let mut headers = HeaderMap :: new ( ) ;
195218 headers. insert ( "Content-Type" , "application/json" . parse ( ) . unwrap ( ) ) ;
196219 headers. insert ( "Access-Control-Allow-Origin" , "*" . parse ( ) . unwrap ( ) ) ;
197- headers. insert ( "Access-Control-Allow-Methods" , "POST, OPTIONS" . parse ( ) . unwrap ( ) ) ;
198- headers. insert ( "Access-Control-Allow-Headers" , "Content-Type" . parse ( ) . unwrap ( ) ) ;
199-
220+ headers. insert (
221+ "Access-Control-Allow-Methods" ,
222+ "POST, OPTIONS" . parse ( ) . unwrap ( ) ,
223+ ) ;
224+ headers. insert (
225+ "Access-Control-Allow-Headers" ,
226+ "Content-Type" . parse ( ) . unwrap ( ) ,
227+ ) ;
228+
200229 // Return the response
201230 Ok ( ( headers, Bytes :: from ( response_bytes) ) . into_response ( ) )
202231 }
@@ -206,24 +235,33 @@ impl Api {
206235 // Create response with CORS headers
207236 let mut headers = HeaderMap :: new ( ) ;
208237 headers. insert ( "Access-Control-Allow-Origin" , "*" . parse ( ) . unwrap ( ) ) ;
209- headers. insert ( "Access-Control-Allow-Methods" , "POST, OPTIONS" . parse ( ) . unwrap ( ) ) ;
210- headers. insert ( "Access-Control-Allow-Headers" , "Content-Type" . parse ( ) . unwrap ( ) ) ;
211-
238+ headers. insert (
239+ "Access-Control-Allow-Methods" ,
240+ "POST, OPTIONS" . parse ( ) . unwrap ( ) ,
241+ ) ;
242+ headers. insert (
243+ "Access-Control-Allow-Headers" ,
244+ "Content-Type" . parse ( ) . unwrap ( ) ,
245+ ) ;
246+
212247 ( headers, "" ) . into_response ( )
213248 }
214249
215250 // Build the router
216251 let app = Router :: new ( )
217- . route ( "/" , post ( move |body : Bytes | handle_json_rpc ( body, zinit. clone ( ) ) ) )
252+ . route (
253+ "/" ,
254+ post ( move |body : Bytes | handle_json_rpc ( body, zinit. clone ( ) ) ) ,
255+ )
218256 . route ( "/" , options ( handle_cors) ) ;
219257
220258 // Run the server
221259 let addr = SocketAddr :: from ( ( [ 0 , 0 , 0 , 0 ] , port) ) ;
222260 info ! ( "Zinit HTTP proxy listening on http://{}" , addr) ;
223-
261+
224262 let listener = TcpListener :: bind ( addr) . await ?;
225263 axum:: serve ( listener, app) . await ?;
226-
264+
227265 Ok ( ( ) )
228266 }
229267
@@ -271,7 +309,7 @@ impl Api {
271309 }
272310 } ;
273311 Ok ( spec)
274- } ,
312+ }
275313
276314 // Service management methods
277315 "service.list" => Self :: list ( zinit) . await ,
@@ -432,11 +470,14 @@ impl Api {
432470 CONFIG_ERROR
433471 } else if err. to_string ( ) . contains ( "shutting down" ) {
434472 SHUTTING_DOWN
435- } else if err. to_string ( ) . contains ( "Service" ) && err. to_string ( ) . contains ( "already exists" ) {
473+ } else if err. to_string ( ) . contains ( "Service" )
474+ && err. to_string ( ) . contains ( "already exists" )
475+ {
436476 SERVICE_ALREADY_EXISTS
437- } else if err. to_string ( ) . contains ( "Failed to" ) &&
438- ( err. to_string ( ) . contains ( "service file" ) ||
439- err. to_string ( ) . contains ( "configuration" ) ) {
477+ } else if err. to_string ( ) . contains ( "Failed to" )
478+ && ( err. to_string ( ) . contains ( "service file" )
479+ || err. to_string ( ) . contains ( "configuration" ) )
480+ {
440481 SERVICE_FILE_ERROR
441482 } else {
442483 INTERNAL_ERROR
@@ -873,7 +914,7 @@ impl Api {
873914 use std:: io:: Write ;
874915
875916 let name = name. as_ref ( ) ;
876-
917+
877918 // Validate service name (no path traversal, valid characters)
878919 if name. contains ( '/' ) || name. contains ( '\\' ) || name. contains ( '.' ) {
879920 bail ! ( "Invalid service name: must not contain '/', '\\ ', or '.'" ) ;
@@ -892,18 +933,20 @@ impl Api {
892933 . context ( "Failed to convert service configuration to YAML" ) ?;
893934
894935 // Write the YAML content to the file
895- let mut file = fs:: File :: create ( & file_path)
896- . context ( "Failed to create service file" ) ?;
936+ let mut file = fs:: File :: create ( & file_path) . context ( "Failed to create service file" ) ?;
897937 file. write_all ( yaml_content. as_bytes ( ) )
898938 . context ( "Failed to write service configuration" ) ?;
899939
900- Ok ( Value :: String ( format ! ( "Service '{}' created successfully" , name) ) )
940+ Ok ( Value :: String ( format ! (
941+ "Service '{}' created successfully" ,
942+ name
943+ ) ) )
901944 }
902945 async fn delete_service < S : AsRef < str > > ( name : S , zinit : ZInit ) -> Result < Value > {
903946 use std:: fs;
904947
905948 let name = name. as_ref ( ) ;
906-
949+
907950 // Validate service name (no path traversal, valid characters)
908951 if name. contains ( '/' ) || name. contains ( '\\' ) || name. contains ( '.' ) {
909952 bail ! ( "Invalid service name: must not contain '/', '\\ ', or '.'" ) ;
@@ -918,16 +961,18 @@ impl Api {
918961 }
919962
920963 // Delete the file
921- fs:: remove_file ( & file_path)
922- . context ( "Failed to delete service file" ) ?;
964+ fs:: remove_file ( & file_path) . context ( "Failed to delete service file" ) ?;
923965
924- Ok ( Value :: String ( format ! ( "Service '{}' deleted successfully" , name) ) )
966+ Ok ( Value :: String ( format ! (
967+ "Service '{}' deleted successfully" ,
968+ name
969+ ) ) )
925970 }
926971 async fn get_service < S : AsRef < str > > ( name : S , zinit : ZInit ) -> Result < Value > {
927972 use std:: fs;
928973
929974 let name = name. as_ref ( ) ;
930-
975+
931976 // Validate service name (no path traversal, valid characters)
932977 if name. contains ( '/' ) || name. contains ( '\\' ) || name. contains ( '.' ) {
933978 bail ! ( "Invalid service name: must not contain '/', '\\ ', or '.'" ) ;
@@ -942,16 +987,15 @@ impl Api {
942987 }
943988
944989 // Read the file content
945- let yaml_content = fs:: read_to_string ( & file_path)
946- . context ( "Failed to read service file" ) ?;
990+ let yaml_content = fs:: read_to_string ( & file_path) . context ( "Failed to read service file" ) ?;
947991
948992 // Parse YAML to JSON
949- let yaml_value: serde_yaml:: Value = serde_yaml :: from_str ( & yaml_content )
950- . context ( "Failed to parse YAML content" ) ?;
951-
993+ let yaml_value: serde_yaml:: Value =
994+ serde_yaml :: from_str ( & yaml_content ) . context ( "Failed to parse YAML content" ) ?;
995+
952996 // Convert YAML value to JSON value
953- let json_value = serde_json :: to_value ( yaml_value )
954- . context ( "Failed to convert YAML to JSON" ) ?;
997+ let json_value =
998+ serde_json :: to_value ( yaml_value ) . context ( "Failed to convert YAML to JSON" ) ?;
955999
9561000 Ok ( json_value)
9571001 }
@@ -1193,7 +1237,11 @@ impl Client {
11931237 }
11941238
11951239 // Service file operations
1196- pub async fn create_service < S : AsRef < str > > ( & self , name : S , content : serde_json:: Map < String , Value > ) -> Result < String > {
1240+ pub async fn create_service < S : AsRef < str > > (
1241+ & self ,
1242+ name : S ,
1243+ content : serde_json:: Map < String , Value > ,
1244+ ) -> Result < String > {
11971245 let params = serde_json:: json!( {
11981246 "name" : name. as_ref( ) ,
11991247 "content" : content
0 commit comments