hypercall_api/handlers/
directives.rs1use axum::{
4 extract::{Path, Query, State},
5 http::StatusCode,
6};
7use serde::Deserialize;
8use tracing::error;
9
10use super::AppState;
11use crate::error::ApiError;
12use crate::sonic_json::SonicJson;
13use hypercall_db::AsyncDirectiveOutboxReader;
14use hypercall_types::api_models::{DirectiveStatusResponse, WithdrawalHistoryResponse};
15use hypercall_types::WalletAddress;
16
17pub async fn get_directive_status(
21 State(state): State<AppState>,
22 Path(directive_id): Path<String>,
23) -> Result<(StatusCode, SonicJson<DirectiveStatusResponse>), ApiError> {
24 let directive_reader: &dyn AsyncDirectiveOutboxReader = state.db.as_ref();
25 let row = directive_reader
26 .get_directive_status(&directive_id)
27 .await
28 .map_err(|e| {
29 error!("directive status query failed: {e}");
30 ApiError::internal_error("directive status lookup failed")
31 })?;
32
33 match row {
34 Some(row) => {
35 let created_at = row.created_ts_ms.map(|ms| {
36 chrono::DateTime::from_timestamp_millis(ms)
37 .map(|dt| dt.to_rfc3339())
38 .unwrap_or_else(|| ms.to_string())
39 });
40
41 Ok((
42 StatusCode::OK,
43 SonicJson(DirectiveStatusResponse {
44 directive_id: row.directive_id,
45 action_key: row.action_key,
46 domain_status: row.domain_status,
47 delivery_status: row.delivery_status,
48 tx_hash: row.tx_hash,
49 created_at,
50 }),
51 ))
52 }
53 None => Err(ApiError::not_found(format!(
54 "directive {} not found",
55 directive_id
56 ))),
57 }
58}
59
60#[derive(Deserialize)]
62pub struct WithdrawalHistoryQuery {
63 pub wallet: WalletAddress,
64 #[serde(default)]
65 pub limit: Option<i64>,
66}
67
68fn default_withdrawal_limit() -> i64 {
69 50
70}
71
72const MAX_WITHDRAWAL_LIMIT: i64 = 100;
73
74pub async fn get_withdrawal_history(
78 State(state): State<AppState>,
79 Query(params): Query<WithdrawalHistoryQuery>,
80) -> Result<(StatusCode, SonicJson<WithdrawalHistoryResponse>), ApiError> {
81 let limit = params
82 .limit
83 .unwrap_or_else(default_withdrawal_limit)
84 .min(MAX_WITHDRAWAL_LIMIT)
85 .max(1);
86 let wallet = params.wallet;
87
88 let directive_reader: &dyn AsyncDirectiveOutboxReader = state.db.as_ref();
89 let rows = directive_reader
90 .get_withdrawal_history(&wallet, limit)
91 .await
92 .map_err(|e| {
93 error!("withdrawal history query failed: {e}");
94 ApiError::internal_error("withdrawal history lookup failed")
95 })?;
96
97 let withdrawals = rows
98 .into_iter()
99 .map(|row| {
100 let created_at = row.created_ts_ms.map(|ms| {
101 chrono::DateTime::from_timestamp_millis(ms)
102 .map(|dt| dt.to_rfc3339())
103 .unwrap_or_else(|| ms.to_string())
104 });
105
106 DirectiveStatusResponse {
107 directive_id: row.directive_id,
108 action_key: row.action_key,
109 domain_status: row.domain_status,
110 delivery_status: row.delivery_status,
111 tx_hash: row.tx_hash,
112 created_at,
113 }
114 })
115 .collect();
116
117 Ok((
118 StatusCode::OK,
119 SonicJson(WithdrawalHistoryResponse { withdrawals }),
120 ))
121}