1use axum::extract::State;
2use serde::{Deserialize, Serialize};
3use std::sync::Arc;
4use validator::{Validate, ValidationError};
5
6use ns_axum_web::erring::{HTTPError, SuccessResponse};
7use ns_axum_web::object::PackObject;
8use ns_protocol::ns;
9
10use crate::db::scylladb::ScyllaDB;
11use crate::indexer::{Indexer, IndexerState};
12
13mod inscription;
14mod name;
15mod service;
16mod utxo;
17
18pub use inscription::InscriptionAPI;
19pub use name::NameAPI;
20pub use service::ServiceAPI;
21pub use utxo::UtxoAPI;
22
23#[derive(Serialize, Deserialize)]
24pub struct AppVersion {
25 pub name: String,
26 pub version: String,
27}
28
29#[derive(Serialize, Deserialize)]
30pub struct AppHealth {
31 pub block_height: u64,
32 pub inscription_height: u64,
33}
34
35pub struct IndexerAPI {
36 pub(crate) scylla: Arc<ScyllaDB>,
37 pub(crate) state: Arc<IndexerState>,
38}
39
40impl IndexerAPI {
41 pub fn new(indexer: Arc<Indexer>) -> Self {
42 Self {
43 scylla: indexer.scylla.clone(),
44 state: indexer.state.clone(),
45 }
46 }
47}
48
49pub async fn version(
50 to: PackObject<()>,
51 State(_): State<Arc<IndexerAPI>>,
52) -> PackObject<AppVersion> {
53 to.with(AppVersion {
54 name: crate::APP_NAME.to_string(),
55 version: crate::APP_VERSION.to_string(),
56 })
57}
58
59pub async fn healthz(
60 to: PackObject<()>,
61 State(app): State<Arc<IndexerAPI>>,
62) -> Result<PackObject<SuccessResponse<AppHealth>>, HTTPError> {
63 let last_accepted_state = app.state.last_accepted.read().await;
64 let (block_height, height) = match *last_accepted_state {
65 Some(ref last_accepted) => (last_accepted.block_height, last_accepted.height),
66 None => (0, 0),
67 };
68 Ok(to.with(SuccessResponse::new(AppHealth {
69 block_height,
70 inscription_height: height,
71 })))
72}
73
74#[derive(Debug, Deserialize, Validate)]
75pub struct QueryName {
76 #[validate(custom = "validate_name")]
77 pub name: String,
78 #[validate(range(min = 0))]
79 pub sequence: Option<i64>,
80 #[validate(range(min = 0))]
81 pub code: Option<i64>,
82}
83
84#[derive(Debug, Deserialize, Validate)]
85pub struct QueryHeight {
86 #[validate(range(min = 0))]
87 pub height: i64,
88}
89
90#[derive(Debug, Deserialize, Validate)]
91pub struct QueryNamePagination {
92 #[validate(custom = "validate_name")]
93 pub name: String,
94 pub page_token: Option<i64>,
95 #[validate(range(min = 2, max = 1000))]
96 pub page_size: Option<u16>,
97}
98
99#[derive(Debug, Deserialize, Validate)]
100pub struct QueryPubkey {
101 pub pubkey: String,
102}
103
104#[derive(Debug, Deserialize, Validate)]
105pub struct QueryAddress {
106 pub address: String,
107}
108
109fn validate_name(name: &str) -> Result<(), ValidationError> {
110 if !ns::valid_name(name) {
111 return Err(ValidationError::new("invalid name"));
112 }
113
114 Ok(())
115}