@@ -10,32 +10,74 @@ use axum::{
1010use tower_http:: cors:: CorsLayer ;
1111
1212use super :: request:: handle_request;
13+ use super :: status:: { DebugStatusResponse , PublicStatusResponse } ;
1314use super :: state:: { AppState , SecretsServerState } ;
1415use crate :: secrets:: SecretsSnapshotWire ;
1516use crate :: wlog;
1617
1718/// Spawn the user-request server. Runs until the process exits.
1819pub async fn run_user_server ( port : u16 , state : AppState ) {
19- let app = Router :: new ( )
20+ serve ( port, user_router ( state) , "http-server (user)" ) . await ;
21+ }
22+
23+ pub async fn run_user_admin_server ( port : u16 , state : AppState ) {
24+ serve_loopback ( port, user_admin_router ( state) , "http-server (user-admin)" ) . await ;
25+ }
26+
27+ pub async fn run_secrets_server ( port : u16 , state : SecretsServerState ) {
28+ serve ( port, secrets_router ( state) , "http-server (secrets)" ) . await ;
29+ }
30+
31+ pub async fn run_secrets_admin_server ( port : u16 , state : SecretsServerState ) {
32+ serve_loopback (
33+ port,
34+ secrets_admin_router ( state) ,
35+ "http-server (secrets-admin)" ,
36+ )
37+ . await ;
38+ }
39+
40+ fn user_router ( state : AppState ) -> Router {
41+ Router :: new ( )
2042 . route ( "/" , post ( handle_request) )
2143 . route ( "/healthz" , get ( handle_healthz) )
44+ . route ( "/status" , get ( handle_user_status) )
2245 . with_state ( state)
23- . layer ( CorsLayer :: permissive ( ) ) ;
24- serve ( port, app, "http-server (user)" ) . await ;
46+ . layer ( CorsLayer :: permissive ( ) )
2547}
2648
27- pub async fn run_secrets_server ( port : u16 , state : SecretsServerState ) {
28- let app = Router :: new ( )
49+ fn user_admin_router ( state : AppState ) -> Router {
50+ Router :: new ( )
51+ . route ( "/debug/status" , get ( handle_user_debug_status) )
52+ . with_state ( state)
53+ }
54+
55+ fn secrets_router ( state : SecretsServerState ) -> Router {
56+ Router :: new ( )
2957 . route ( "/secrets" , get ( handle_get_secrets) )
3058 . route ( "/healthz" , get ( handle_secrets_healthz) )
31- . with_state ( state) ;
32- serve ( port, app, "http-server (secrets)" ) . await ;
59+ . route ( "/status" , get ( handle_secrets_status) )
60+ . with_state ( state)
61+ }
62+
63+ fn secrets_admin_router ( state : SecretsServerState ) -> Router {
64+ Router :: new ( )
65+ . route ( "/debug/status" , get ( handle_secrets_debug_status) )
66+ . with_state ( state)
3367}
3468
3569async fn handle_healthz ( ) -> StatusCode {
3670 StatusCode :: OK
3771}
3872
73+ async fn handle_user_status ( State ( state) : State < AppState > ) -> Json < PublicStatusResponse > {
74+ Json ( state. status . public_response ( ) )
75+ }
76+
77+ async fn handle_user_debug_status ( State ( state) : State < AppState > ) -> Json < DebugStatusResponse > {
78+ Json ( state. status . debug_response ( ) . await )
79+ }
80+
3981async fn handle_get_secrets ( State ( state) : State < SecretsServerState > ) -> Json < SecretsSnapshotWire > {
4082 Json ( state. local . snapshot_wire ( ) . await )
4183}
@@ -44,8 +86,27 @@ async fn handle_secrets_healthz() -> StatusCode {
4486 StatusCode :: OK
4587}
4688
89+ async fn handle_secrets_status (
90+ State ( state) : State < SecretsServerState > ,
91+ ) -> Json < PublicStatusResponse > {
92+ Json ( state. status . public_response ( ) )
93+ }
94+
95+ async fn handle_secrets_debug_status (
96+ State ( state) : State < SecretsServerState > ,
97+ ) -> Json < DebugStatusResponse > {
98+ Json ( state. status . debug_response ( ) . await )
99+ }
100+
47101async fn serve ( port : u16 , app : Router , label : & str ) {
48- let addr = format ! ( "0.0.0.0:{}" , port) ;
102+ serve_addr ( format ! ( "0.0.0.0:{}" , port) , app, label) . await
103+ }
104+
105+ async fn serve_loopback ( port : u16 , app : Router , label : & str ) {
106+ serve_addr ( format ! ( "127.0.0.1:{}" , port) , app, label) . await
107+ }
108+
109+ async fn serve_addr ( addr : String , app : Router , label : & str ) {
49110 let listener = match tokio:: net:: TcpListener :: bind ( & addr) . await {
50111 Ok ( l) => l,
51112 Err ( e) => {
@@ -58,3 +119,106 @@ async fn serve(port: u16, app: Router, label: &str) {
58119 wlog ! ( "{}: serve error: {}" , label, e) ;
59120 }
60121}
122+
123+ #[ cfg( test) ]
124+ mod tests {
125+ use std:: { collections:: HashMap , sync:: Arc } ;
126+
127+ use axum:: { body:: Body , http:: Request } ;
128+ use tokio:: sync:: { RwLock , Semaphore } ;
129+ use tower:: ServiceExt ;
130+ use vss_common:: AptosRpc ;
131+
132+ use super :: super :: status:: { NodeStatus , PublicNodeConfig } ;
133+ use super :: * ;
134+ use crate :: secrets:: { LocalSecrets , SecretsProvider } ;
135+ use crate :: ChainRpcConfig ;
136+
137+ fn rpc ( label : & str ) -> AptosRpc {
138+ AptosRpc :: new ( format ! ( "https://{}.example/v1" , label) )
139+ }
140+
141+ fn chain_rpc_config ( ) -> ChainRpcConfig {
142+ ChainRpcConfig {
143+ aptos_mainnet : rpc ( "mainnet" ) ,
144+ aptos_testnet : rpc ( "testnet" ) ,
145+ aptos_localnet : rpc ( "localnet" ) ,
146+ aptos_shelby_private_beta : Some ( rpc ( "shelby" ) ) ,
147+ solana_mainnet_beta : "https://solana-mainnet.example" . to_string ( ) ,
148+ solana_testnet : "https://solana-testnet.example" . to_string ( ) ,
149+ solana_devnet : "https://solana-devnet.example" . to_string ( ) ,
150+ solana_client : reqwest:: Client :: new ( ) ,
151+ }
152+ }
153+
154+ fn local_secrets ( ) -> LocalSecrets {
155+ LocalSecrets {
156+ shares : Arc :: new ( RwLock :: new ( HashMap :: new ( ) ) ) ,
157+ }
158+ }
159+
160+ fn node_status ( ) -> Arc < NodeStatus > {
161+ Arc :: new ( NodeStatus :: new ( PublicNodeConfig :: new ( "handler" ) , Vec :: new ( ) ) )
162+ }
163+
164+ fn app_state ( ) -> AppState {
165+ AppState {
166+ provider : Arc :: new ( SecretsProvider :: Local ( local_secrets ( ) ) ) ,
167+ chain_rpc : Arc :: new ( chain_rpc_config ( ) ) ,
168+ concurrency : Arc :: new ( Semaphore :: new ( 1 ) ) ,
169+ pke_dk_bytes : Arc :: new ( Vec :: new ( ) ) ,
170+ status : node_status ( ) ,
171+ }
172+ }
173+
174+ fn secrets_state ( ) -> SecretsServerState {
175+ SecretsServerState {
176+ local : local_secrets ( ) ,
177+ status : node_status ( ) ,
178+ }
179+ }
180+
181+ async fn get_status ( app : Router , path : & str ) -> StatusCode {
182+ app. oneshot (
183+ Request :: builder ( )
184+ . uri ( path)
185+ . body ( Body :: empty ( ) )
186+ . unwrap ( ) ,
187+ )
188+ . await
189+ . unwrap ( )
190+ . status ( )
191+ }
192+
193+ #[ tokio:: test]
194+ async fn public_user_router_does_not_expose_debug_status ( ) {
195+ assert_eq ! (
196+ get_status( user_router( app_state( ) ) , "/debug/status" ) . await ,
197+ StatusCode :: NOT_FOUND
198+ ) ;
199+ }
200+
201+ #[ tokio:: test]
202+ async fn public_secrets_router_does_not_expose_debug_status ( ) {
203+ assert_eq ! (
204+ get_status( secrets_router( secrets_state( ) ) , "/debug/status" ) . await ,
205+ StatusCode :: NOT_FOUND
206+ ) ;
207+ }
208+
209+ #[ tokio:: test]
210+ async fn user_admin_router_exposes_debug_status ( ) {
211+ assert_eq ! (
212+ get_status( user_admin_router( app_state( ) ) , "/debug/status" ) . await ,
213+ StatusCode :: OK
214+ ) ;
215+ }
216+
217+ #[ tokio:: test]
218+ async fn secrets_admin_router_exposes_debug_status ( ) {
219+ assert_eq ! (
220+ get_status( secrets_admin_router( secrets_state( ) ) , "/debug/status" ) . await ,
221+ StatusCode :: OK
222+ ) ;
223+ }
224+ }
0 commit comments