@@ -79,6 +79,10 @@ struct Search {
79
79
#[ folder = "static" ]
80
80
struct StaticAssets ;
81
81
82
+ lazy_static ! {
83
+ static ref SAT_AT_INDEX_PATH : Regex = Regex :: new( r"^/r/sat/[^/]+/at/[^/]+$" ) . unwrap( ) ;
84
+ }
85
+
82
86
#[ derive( Debug , Parser , Clone ) ]
83
87
pub struct Server {
84
88
#[ arg(
@@ -273,7 +277,6 @@ impl Server {
273
277
get ( r:: parent_inscriptions_paginated) ,
274
278
)
275
279
. route ( "/r/sat/{sat_number}" , get ( r:: sat) )
276
- . route ( "/r/sat/{sat_number}/at/{index}" , get ( r:: sat_at_index) )
277
280
. route ( "/r/sat/{sat_number}/{page}" , get ( r:: sat_paginated) )
278
281
. route ( "/r/tx/{txid}" , get ( r:: tx) )
279
282
. route (
@@ -291,11 +294,12 @@ impl Server {
291
294
)
292
295
. route ( "/r/inscription/{inscription_id}" , get ( r:: inscription) )
293
296
. route ( "/r/metadata/{inscription_id}" , get ( r:: metadata) )
297
+ . route ( "/r/sat/{sat_number}/at/{index}" , get ( r:: sat_at_index) )
294
298
. route (
295
299
"/r/sat/{sat_number}/at/{index}/content" ,
296
300
get ( r:: sat_at_index_content) ,
297
301
)
298
- . layer ( axum:: middleware:: from_fn ( Self :: proxy_fallback ) ) ;
302
+ . layer ( axum:: middleware:: from_fn ( Self :: proxy_layer ) ) ;
299
303
300
304
let router = router. merge ( proxiable_routes) ;
301
305
@@ -529,22 +533,37 @@ impl Server {
529
533
Ok ( acceptor)
530
534
}
531
535
532
- async fn proxy_fallback (
533
- Extension ( server_config) : Extension < Arc < ServerConfig > > ,
536
+ async fn proxy_layer (
537
+ server_config : Extension < Arc < ServerConfig > > ,
534
538
request : http:: Request < axum:: body:: Body > ,
535
539
next : axum:: middleware:: Next ,
536
540
) -> ServerResult {
537
- let mut path = request. uri ( ) . path ( ) . to_string ( ) ;
538
-
539
- path. remove ( 0 ) ; // remove leading slash
541
+ let path = request. uri ( ) . path ( ) . to_owned ( ) ;
540
542
541
543
let response = next. run ( request) . await ;
542
- let status = response. status ( ) ;
543
544
544
- if let Some ( proxy) = server_config. proxy . as_ref ( ) {
545
- if status == StatusCode :: NOT_FOUND {
545
+ if let Some ( proxy) = & server_config. proxy {
546
+ if response . status ( ) == StatusCode :: NOT_FOUND {
546
547
return task:: block_in_place ( || Server :: proxy ( proxy, & path) ) ;
547
548
}
549
+
550
+ // `/r/sat/<SAT_NUMBER>/at/<INDEX>` does not return a 404 when no
551
+ // inscription is present, so we must deserialize and check the body.
552
+ if SAT_AT_INDEX_PATH . is_match ( & path) {
553
+ let ( parts, body) = response. into_parts ( ) ;
554
+
555
+ let bytes = axum:: body:: to_bytes ( body, usize:: MAX )
556
+ . await
557
+ . map_err ( |err| anyhow ! ( err) ) ?;
558
+
559
+ if let Ok ( api:: SatInscription { id : None } ) =
560
+ serde_json:: from_slice :: < api:: SatInscription > ( & bytes)
561
+ {
562
+ return task:: block_in_place ( || Server :: proxy ( proxy, & path) ) ;
563
+ }
564
+
565
+ return Ok ( Response :: from_parts ( parts, axum:: body:: Body :: from ( bytes) ) ) ;
566
+ }
548
567
}
549
568
550
569
Ok ( response)
@@ -1856,7 +1875,7 @@ impl Server {
1856
1875
1857
1876
fn proxy ( proxy : & Url , path : & str ) -> ServerResult < Response > {
1858
1877
let response = reqwest:: blocking:: Client :: new ( )
1859
- . get ( format ! ( "{}{}" , proxy, path) )
1878
+ . get ( format ! ( "{}{}" , proxy, & path[ 1 .. ] ) )
1860
1879
. send ( )
1861
1880
. map_err ( |err| anyhow ! ( err) ) ?;
1862
1881
@@ -7039,6 +7058,66 @@ next
7039
7058
) ;
7040
7059
}
7041
7060
7061
+ #[ test]
7062
+ fn sat_at_index_proxy ( ) {
7063
+ let server = TestServer :: builder ( )
7064
+ . index_sats ( )
7065
+ . chain ( Chain :: Regtest )
7066
+ . build ( ) ;
7067
+
7068
+ server. mine_blocks ( 1 ) ;
7069
+
7070
+ let inscription = Inscription {
7071
+ content_type : Some ( "text/html" . into ( ) ) ,
7072
+ body : Some ( "foo" . into ( ) ) ,
7073
+ ..default ( )
7074
+ } ;
7075
+
7076
+ let txid = server. core . broadcast_tx ( TransactionTemplate {
7077
+ inputs : & [ ( 1 , 0 , 0 , inscription. to_witness ( ) ) ] ,
7078
+ ..default ( )
7079
+ } ) ;
7080
+
7081
+ server. mine_blocks ( 1 ) ;
7082
+
7083
+ let id = InscriptionId { txid, index : 0 } ;
7084
+ let ordinal: u64 = 5000000000 ;
7085
+
7086
+ pretty_assert_eq ! (
7087
+ server. get_json:: <api:: SatInscription >( format!( "/r/sat/{ordinal}/at/-1" ) ) ,
7088
+ api:: SatInscription { id: Some ( id) }
7089
+ ) ;
7090
+
7091
+ let server_with_proxy = TestServer :: builder ( )
7092
+ . chain ( Chain :: Regtest )
7093
+ . server_option ( "--proxy" , server. url . as_ref ( ) )
7094
+ . build ( ) ;
7095
+ let sat_indexed_server_with_proxy = TestServer :: builder ( )
7096
+ . index_sats ( )
7097
+ . chain ( Chain :: Regtest )
7098
+ . server_option ( "--proxy" , server. url . as_ref ( ) )
7099
+ . build ( ) ;
7100
+
7101
+ server_with_proxy. mine_blocks ( 1 ) ;
7102
+ sat_indexed_server_with_proxy. mine_blocks ( 1 ) ;
7103
+
7104
+ pretty_assert_eq ! (
7105
+ server. get_json:: <api:: SatInscription >( format!( "/r/sat/{ordinal}/at/-1" ) ) ,
7106
+ api:: SatInscription { id: Some ( id) }
7107
+ ) ;
7108
+
7109
+ pretty_assert_eq ! (
7110
+ server_with_proxy. get_json:: <api:: SatInscription >( format!( "/r/sat/{ordinal}/at/-1" ) ) ,
7111
+ api:: SatInscription { id: Some ( id) }
7112
+ ) ;
7113
+
7114
+ pretty_assert_eq ! (
7115
+ sat_indexed_server_with_proxy
7116
+ . get_json:: <api:: SatInscription >( format!( "/r/sat/{ordinal}/at/-1" ) ) ,
7117
+ api:: SatInscription { id: Some ( id) }
7118
+ ) ;
7119
+ }
7120
+
7042
7121
#[ test]
7043
7122
fn sat_at_index_content_proxy ( ) {
7044
7123
let server = TestServer :: builder ( )
0 commit comments