hypercall_admin/
testnet.rs1use axum::extract::State;
8use axum::http::StatusCode;
9use serde::{Deserialize, Serialize};
10use utoipa::ToSchema;
11
12use hypercall_runtime_api::error::ApiError;
13use hypercall_runtime_api::sonic_json::SonicJson;
14use hypercall_types::api_models::ApiResponse;
15use hypercall_types::WalletAddress;
16
17use crate::state::{AdminState, ENGINE_RESPONSE_TIMEOUT};
18
19#[derive(Debug, Deserialize, ToSchema)]
20pub struct TestAgentAuthorizationRequest {
21 #[schema(value_type = String)]
22 pub wallet: WalletAddress,
23 #[schema(value_type = String)]
24 pub agent: WalletAddress,
25 pub approve: bool,
26 pub nonce: u64,
27}
28
29#[derive(Debug, Serialize, ToSchema)]
30pub struct TestAgentAuthorizationResponse {
31 #[schema(value_type = String)]
32 pub wallet: WalletAddress,
33 #[schema(value_type = String)]
34 pub agent: WalletAddress,
35 pub approved: bool,
36}
37
38#[derive(Debug, Serialize, ToSchema)]
40pub struct TestAgentAuthorizationApiResponse {
41 pub success: bool,
43 pub data: Option<TestAgentAuthorizationResponse>,
44 pub error: Option<String>,
46}
47
48#[utoipa::path(
50 post,
51 path = "/monitoring/test/agent-authorization",
52 request_body = TestAgentAuthorizationRequest,
53 responses(
54 (status = 200, description = "Agent authorization applied", body = TestAgentAuthorizationApiResponse),
55 (status = 403, description = "Not enabled (production mode)"),
56 (status = 503, description = "Engine communication failure"),
57 (status = 504, description = "Engine response timeout")
58 ),
59 tag = "Monitoring",
60 security(("admin_key" = []))
61)]
62pub async fn apply_agent_authorization(
63 State(state): State<AdminState>,
64 SonicJson(request): SonicJson<TestAgentAuthorizationRequest>,
65) -> Result<SonicJson<ApiResponse<TestAgentAuthorizationResponse>>, ApiError> {
66 if !state.runtime_config.testnet_mode {
67 return Err(ApiError::forbidden(
68 "Agent authorization test endpoint requires testnet mode",
69 ));
70 }
71
72 let (applied_tx, applied_rx) = tokio::sync::oneshot::channel();
73 state
74 .agent_auth_sender
75 .send(hypercall_runtime_api::AgentAuthRequest {
76 wallet: request.wallet,
77 agent: request.agent,
78 approve: request.approve,
79 expires_at_ms: None,
80 nonce: Some(request.nonce),
81 applied_tx,
82 })
83 .await
84 .map_err(|_| {
85 ApiError::new(
86 StatusCode::SERVICE_UNAVAILABLE,
87 "service_unavailable",
88 "agent authorization engine path closed",
89 )
90 })?;
91
92 match tokio::time::timeout(ENGINE_RESPONSE_TIMEOUT, applied_rx).await {
93 Ok(Ok(Ok(()))) => {}
94 Ok(Ok(Err(error))) => return Ok(SonicJson(ApiResponse::error(error))),
95 Ok(Err(_)) => {
96 return Err(ApiError::new(
97 StatusCode::SERVICE_UNAVAILABLE,
98 "service_unavailable",
99 "agent authorization apply dropped",
100 ))
101 }
102 Err(_) => {
103 return Err(ApiError::gateway_timeout(
104 "agent authorization apply timed out",
105 ))
106 }
107 }
108
109 Ok(SonicJson(ApiResponse::success(
110 TestAgentAuthorizationResponse {
111 wallet: request.wallet,
112 agent: request.agent,
113 approved: request.approve,
114 },
115 )))
116}