From 431a17c451aba2a530d6aa223db36c39b9fb0aa8 Mon Sep 17 00:00:00 2001 From: Iris Date: Wed, 20 Sep 2023 11:01:37 +0200 Subject: [PATCH 1/3] feat: add get_metahash endpoint for autorenewal --- src/endpoints/renewal/get_metahash.rs | 76 +++++++++++++++++++++ src/endpoints/renewal/get_renewal_data.rs | 5 +- src/endpoints/renewal/mod.rs | 1 + src/endpoints/stats/count_addrs.rs | 10 ++- src/endpoints/stats/count_club_domains.rs | 14 ++-- src/endpoints/stats/count_created.rs | 11 ++- src/endpoints/stats/count_domains.rs | 11 ++- src/endpoints/stats/count_ids.rs | 7 +- src/endpoints/stats/expired_club_domains.rs | 10 ++- src/main.rs | 4 ++ 10 files changed, 132 insertions(+), 17 deletions(-) create mode 100644 src/endpoints/renewal/get_metahash.rs diff --git a/src/endpoints/renewal/get_metahash.rs b/src/endpoints/renewal/get_metahash.rs new file mode 100644 index 0000000..95006db --- /dev/null +++ b/src/endpoints/renewal/get_metahash.rs @@ -0,0 +1,76 @@ +use crate::{ + models::AppState, + utils::{get_error, to_hex}, +}; +use axum::{ + extract::{Query, State}, + http::{HeaderMap, HeaderValue, StatusCode}, + response::{IntoResponse, Json}, +}; +use futures::StreamExt; +use mongodb::bson::{doc, Bson}; +use serde::{Deserialize, Serialize}; +use starknet::core::types::FieldElement; +use std::sync::Arc; + +#[derive(Serialize)] +pub struct GetMetaHashData { + meta_hash: String, +} + +#[derive(Deserialize)] +pub struct GetMetaHashQuery { + addr: FieldElement, +} + +pub async fn handler( + State(state): State>, + Query(query): Query, +) -> impl IntoResponse { + let renew_collection = state + .sales_db + .collection::("sales"); + + let documents = renew_collection + .find( + doc! { + "payer": to_hex(&query.addr), + "$or": [ + { "_cursor.to": { "$exists": false } }, + { "_cursor.to": Bson::Null }, + ], + }, + None, + ) + .await; + + match documents { + Ok(mut cursor) => { + let mut headers = HeaderMap::new(); + headers.insert("Cache-Control", HeaderValue::from_static("max-age=30")); + + if let Some(result) = cursor.next().await { + match result { + Ok(res) => { + let meta_hash_str = res.get_str("meta_hash").unwrap(); + let res = GetMetaHashData { + meta_hash: meta_hash_str.to_string(), + }; + (StatusCode::OK, headers, Json(res)).into_response() + } + Err(e) => get_error(format!("Error while processing the document: {:?}", e)), + } + } else { + ( + StatusCode::OK, + headers, + Json(GetMetaHashData { + meta_hash: "".to_string(), + }), + ) + .into_response() + } + } + Err(_) => get_error("Error while fetching from database".to_string()), + } +} diff --git a/src/endpoints/renewal/get_renewal_data.rs b/src/endpoints/renewal/get_renewal_data.rs index 5842cf8..6f02e1b 100644 --- a/src/endpoints/renewal/get_renewal_data.rs +++ b/src/endpoints/renewal/get_renewal_data.rs @@ -37,7 +37,10 @@ pub async fn handler( doc! { "renewer_address": to_hex(&query.addr), "domain": query.domain, - "_cursor.to": null, + "$or": [ + { "_cursor.to": { "$exists": false } }, + { "_cursor.to": null }, + ], }, None, ) diff --git a/src/endpoints/renewal/mod.rs b/src/endpoints/renewal/mod.rs index f47c9fd..965d28b 100644 --- a/src/endpoints/renewal/mod.rs +++ b/src/endpoints/renewal/mod.rs @@ -1 +1,2 @@ +pub mod get_metahash; pub mod get_renewal_data; diff --git a/src/endpoints/stats/count_addrs.rs b/src/endpoints/stats/count_addrs.rs index 5cb5247..31a4605 100644 --- a/src/endpoints/stats/count_addrs.rs +++ b/src/endpoints/stats/count_addrs.rs @@ -6,7 +6,7 @@ use axum::{ Json, }; use futures::StreamExt; -use mongodb::bson::doc; +use mongodb::bson::{doc, Bson}; use serde::{Deserialize, Serialize}; use std::sync::Arc; @@ -33,7 +33,13 @@ pub async fn handler( let aggregate_cursor = domain_collection .aggregate( vec![ - doc! { "$match": { "_cursor.to": null, "creation_date": { "$gte": query.since } }}, + doc! { "$match": { + "creation_date": { "$gte": query.since }, + "$or": [ + { "_cursor.to": { "$exists": false } }, + { "_cursor.to": Bson::Null }, + ], + }}, doc! { "$group": { "_id": "$legacy_address" }}, doc! { "$count": "total" }, ], diff --git a/src/endpoints/stats/count_club_domains.rs b/src/endpoints/stats/count_club_domains.rs index 3598c6f..70062ff 100644 --- a/src/endpoints/stats/count_club_domains.rs +++ b/src/endpoints/stats/count_club_domains.rs @@ -6,7 +6,7 @@ use axum::{ Json, }; use futures::TryStreamExt; -use mongodb::bson::{self, doc}; +use mongodb::bson::{self, doc, Bson}; use serde::{Deserialize, Serialize}; use std::sync::Arc; use std::collections::HashMap; @@ -39,7 +39,10 @@ pub async fn handler( vec![ doc! { "$match": { - "_cursor.to": null, + "$or": [ + { "_cursor.to": { "$exists": false } }, + { "_cursor.to": Bson::Null }, + ], // todo: uncomment when there is a creation_date in the collection custom_resolutions // "creation_date": { // "$gte": query.since, @@ -89,10 +92,13 @@ pub async fn handler( let db_output = domain_collection.aggregate(vec![ doc! { "$match": { - "_cursor.to": null, "creation_date": { "$gte": query.since, - } + }, + "$or": [ + { "_cursor.to": { "$exists": false } }, + { "_cursor.to": Bson::Null }, + ], } }, doc! { diff --git a/src/endpoints/stats/count_created.rs b/src/endpoints/stats/count_created.rs index d268292..1b89b14 100644 --- a/src/endpoints/stats/count_created.rs +++ b/src/endpoints/stats/count_created.rs @@ -6,7 +6,7 @@ use axum::{ Json, }; use futures::StreamExt; -use mongodb::bson::doc; +use mongodb::bson::{doc, Bson}; use serde::{Deserialize, Serialize}; use std::sync::Arc; @@ -35,12 +35,17 @@ pub async fn handler( let mut headers = HeaderMap::new(); headers.insert("Cache-Control", HeaderValue::from_static("max-age=60")); - let domain_collection = state.starknetid_db.collection::("domains"); + let domain_collection = state + .starknetid_db + .collection::("domains"); let pipeline = vec![ doc! { "$match": { - "_cursor.to": null, + "$or": [ + { "_cursor.to": { "$exists": false } }, + { "_cursor.to": Bson::Null }, + ], "creation_date": { "$gte": begin_time, "$lte": end_time diff --git a/src/endpoints/stats/count_domains.rs b/src/endpoints/stats/count_domains.rs index 5396480..93ed665 100644 --- a/src/endpoints/stats/count_domains.rs +++ b/src/endpoints/stats/count_domains.rs @@ -5,7 +5,7 @@ use axum::{ response::IntoResponse, Json, }; -use mongodb::bson::doc; +use mongodb::bson::{doc, Bson}; use serde::{Deserialize, Serialize}; use std::sync::Arc; @@ -26,11 +26,16 @@ pub async fn handler( let mut headers = HeaderMap::new(); headers.insert("Cache-Control", HeaderValue::from_static("max-age=60")); - let domain_collection = state.starknetid_db.collection::("domains"); + let domain_collection = state + .starknetid_db + .collection::("domains"); let filter = doc! { "expiry": { "$gte": chrono::Utc::now().timestamp() }, "creation_date": { "$gte": query.since }, - "_cursor.to": { "$eq": null }, + "$or": [ + { "_cursor.to": { "$exists": false } }, + { "_cursor.to": Bson::Null }, + ], }; let total = domain_collection.count_documents(filter, None).await; diff --git a/src/endpoints/stats/count_ids.rs b/src/endpoints/stats/count_ids.rs index a03429d..93ed665 100644 --- a/src/endpoints/stats/count_ids.rs +++ b/src/endpoints/stats/count_ids.rs @@ -5,7 +5,7 @@ use axum::{ response::IntoResponse, Json, }; -use mongodb::bson::doc; +use mongodb::bson::{doc, Bson}; use serde::{Deserialize, Serialize}; use std::sync::Arc; @@ -32,7 +32,10 @@ pub async fn handler( let filter = doc! { "expiry": { "$gte": chrono::Utc::now().timestamp() }, "creation_date": { "$gte": query.since }, - "_cursor.to": { "$eq": null }, + "$or": [ + { "_cursor.to": { "$exists": false } }, + { "_cursor.to": Bson::Null }, + ], }; let total = domain_collection.count_documents(filter, None).await; diff --git a/src/endpoints/stats/expired_club_domains.rs b/src/endpoints/stats/expired_club_domains.rs index ad03394..39fb3b0 100644 --- a/src/endpoints/stats/expired_club_domains.rs +++ b/src/endpoints/stats/expired_club_domains.rs @@ -6,7 +6,10 @@ use axum::{ Json, }; use futures::StreamExt; -use mongodb::{bson::doc, options::AggregateOptions}; +use mongodb::{ + bson::{doc, Bson}, + options::AggregateOptions, +}; use serde::Serialize; use std::sync::Arc; @@ -28,7 +31,10 @@ pub async fn handler(State(state): State>) -> impl IntoResponse { let pipeline = vec![ doc! { "$match": { - "_cursor.to": null, + "$or": [ + { "_cursor.to": { "$exists": false } }, + { "_cursor.to": Bson::Null }, + ], "expiry": { "$lte": current, } diff --git a/src/main.rs b/src/main.rs index 51560ce..203d31e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -126,6 +126,10 @@ async fn main() { "/renewal/get_renewal_data", get(endpoints::renewal::get_renewal_data::handler), ) + .route( + "/renewal/get_metahash", + get(endpoints::renewal::get_metahash::handler), + ) .route("/galxe/verify", post(endpoints::galxe::verify::handler)) .with_state(shared_state) .layer(cors); From 3d4a110bc3bfae85efdae80c643cc125a2920ef1 Mon Sep 17 00:00:00 2001 From: Iris Date: Wed, 20 Sep 2023 12:33:58 +0200 Subject: [PATCH 2/3] fix: find most recent result only --- src/endpoints/renewal/get_metahash.rs | 51 ++++++++++++--------------- 1 file changed, 23 insertions(+), 28 deletions(-) diff --git a/src/endpoints/renewal/get_metahash.rs b/src/endpoints/renewal/get_metahash.rs index 95006db..554fa04 100644 --- a/src/endpoints/renewal/get_metahash.rs +++ b/src/endpoints/renewal/get_metahash.rs @@ -7,8 +7,10 @@ use axum::{ http::{HeaderMap, HeaderValue, StatusCode}, response::{IntoResponse, Json}, }; -use futures::StreamExt; -use mongodb::bson::{doc, Bson}; +use mongodb::{ + bson::{doc, Bson}, + options::FindOneOptions, +}; use serde::{Deserialize, Serialize}; use starknet::core::types::FieldElement; use std::sync::Arc; @@ -31,8 +33,11 @@ pub async fn handler( .sales_db .collection::("sales"); - let documents = renew_collection - .find( + let find_options = FindOneOptions::builder() + .sort(doc! { "timestamp": -1 }) + .build(); + let document = renew_collection + .find_one( doc! { "payer": to_hex(&query.addr), "$or": [ @@ -40,37 +45,27 @@ pub async fn handler( { "_cursor.to": Bson::Null }, ], }, - None, + find_options, ) .await; - match documents { - Ok(mut cursor) => { + if let Ok(sales_doc) = document { + if let Some(doc) = sales_doc { let mut headers = HeaderMap::new(); headers.insert("Cache-Control", HeaderValue::from_static("max-age=30")); - - if let Some(result) = cursor.next().await { - match result { - Ok(res) => { - let meta_hash_str = res.get_str("meta_hash").unwrap(); - let res = GetMetaHashData { - meta_hash: meta_hash_str.to_string(), - }; - (StatusCode::OK, headers, Json(res)).into_response() - } - Err(e) => get_error(format!("Error while processing the document: {:?}", e)), + match doc.get_str("meta_hash") { + Ok(meta_hash_str) => { + let res = GetMetaHashData { + meta_hash: meta_hash_str.to_string(), + }; + (StatusCode::OK, headers, Json(res)).into_response() } - } else { - ( - StatusCode::OK, - headers, - Json(GetMetaHashData { - meta_hash: "".to_string(), - }), - ) - .into_response() + Err(e) => get_error(format!("No meta_hash found: {:?}", e)), } + } else { + get_error("No results found".to_string()) } - Err(_) => get_error("Error while fetching from database".to_string()), + } else { + get_error("Error while fetching from database".to_string()) } } From 3624bb52670d863f63d12caeb79de0608707613d Mon Sep 17 00:00:00 2001 From: Iris Date: Wed, 20 Sep 2023 12:49:16 +0200 Subject: [PATCH 3/3] fix: add mission endpoint count_renewed --- src/endpoints/stats/count_renewed.rs | 109 +++++++++++++++++++++++++++ src/endpoints/stats/mod.rs | 1 + src/main.rs | 4 + 3 files changed, 114 insertions(+) create mode 100644 src/endpoints/stats/count_renewed.rs diff --git a/src/endpoints/stats/count_renewed.rs b/src/endpoints/stats/count_renewed.rs new file mode 100644 index 0000000..c905199 --- /dev/null +++ b/src/endpoints/stats/count_renewed.rs @@ -0,0 +1,109 @@ +use crate::{models::AppState, utils::get_error}; +use axum::{ + extract::{Query, State}, + http::{HeaderMap, HeaderValue, StatusCode}, + response::IntoResponse, + Json, +}; +use futures::StreamExt; +use mongodb::bson::{doc, Bson}; +use serde::{Deserialize, Serialize}; +use std::sync::Arc; + +#[derive(Serialize)] +pub struct CountRenewedData { + from: i64, + count: i32, +} + +#[derive(Deserialize)] +pub struct CountRenewedQuery { + begin: i64, + end: i64, + segments: i64, +} + +pub async fn handler( + State(state): State>, + Query(query): Query, +) -> impl IntoResponse { + let begin_time = query.begin; + let end_time = query.end; + let delta_time = ((end_time as f64 - begin_time as f64) / query.segments as f64).round() as i64; + + if delta_time > 3600 { + let mut headers = HeaderMap::new(); + headers.insert("Cache-Control", HeaderValue::from_static("max-age=60")); + + let domain_collection = state + .starknetid_db + .collection::("renewals"); + + let pipeline = vec![ + doc! { + "$match": { + "$or": [ + { "_cursor.to": { "$exists": false } }, + { "_cursor.to": Bson::Null }, + ], + "timestamp": { + "$gte": begin_time, + "$lte": end_time + } + } + }, + doc! { + "$group": { + "_id": { + "$floor": { + "$sum": [ + { + "$subtract": [ + { + "$subtract": ["$timestamp", begin_time] + }, + { + "$mod": [ + { + "$subtract": ["$timestamp", begin_time] + }, + delta_time + ] + } + ] + }, + begin_time + ] + } + }, + "count": { + "$sum": 1 + } + } + }, + doc! { + "$project": { + "_id": 0, + "from": "$_id", + "count": "$count" + } + }, + ]; + + let cursor = domain_collection.aggregate(pipeline, None).await.unwrap(); + let result: Vec = cursor + .map(|doc| { + let doc = doc.unwrap(); + let from: i64 = doc.get_i64("from").unwrap(); + let count = doc.get_i32("count").unwrap(); + + CountRenewedData { from, count } + }) + .collect::>() + .await; + + (StatusCode::OK, headers, Json(result)).into_response() + } else { + get_error("delta must be greater than 3600 seconds".to_string()) + } +} diff --git a/src/endpoints/stats/mod.rs b/src/endpoints/stats/mod.rs index 4e7c8f8..6b39db8 100644 --- a/src/endpoints/stats/mod.rs +++ b/src/endpoints/stats/mod.rs @@ -3,4 +3,5 @@ pub mod count_club_domains; pub mod count_created; pub mod count_domains; pub mod count_ids; +pub mod count_renewed; pub mod expired_club_domains; diff --git a/src/main.rs b/src/main.rs index 203d31e..379d7fe 100644 --- a/src/main.rs +++ b/src/main.rs @@ -118,6 +118,10 @@ async fn main() { "/stats/expired_club_domains", get(endpoints::stats::expired_club_domains::handler), ) + .route( + "/stats/count_renewed", + get(endpoints::stats::count_renewed::handler), + ) .route( "/starkscan/fetch_nfts", get(endpoints::starkscan::fetch_nfts::handler),