Skip to content

Commit

Permalink
Merge pull request #229 from starknet-id/feat/nimbora-quest-2
Browse files Browse the repository at this point in the history
feat: adding balance tasks
  • Loading branch information
Th0rgal authored Jul 14, 2024
2 parents 5a0df62 + 39468b4 commit 903c865
Show file tree
Hide file tree
Showing 18 changed files with 433 additions and 153 deletions.
1 change: 1 addition & 0 deletions config.template.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ api_endpoint="XXXXXXXXXXXXXXXXX"
api_key="XXXXXXXXXXXXXXXXX"

[quests]
utils_contract = "0x012"
[quests.sithswap]
utils_contract = "0xXXXXXXXXXXXX"
pairs = ["0xXXXXXXXXXXXX"]
Expand Down
5 changes: 3 additions & 2 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ pub_struct!(Clone, Deserialize; ZkLend {
});

pub_struct!(Clone, Deserialize; Quests {
utils_contract: FieldElement,
sithswap: Pairs,
zklend: ZkLend,
jediswap: Pairs,
Expand Down Expand Up @@ -112,8 +113,8 @@ pub enum QuizQuestionType {

impl<'de> Deserialize<'de> for QuizQuestionType {
fn deserialize<D>(deserializer: D) -> Result<QuizQuestionType, D::Error>
where
D: Deserializer<'de>,
where
D: Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
match s.to_lowercase().as_str() {
Expand Down
91 changes: 91 additions & 0 deletions src/endpoints/admin/balance/create_balance.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
use crate::models::{JWTClaims, QuestDocument, QuestTaskDocument};
use crate::utils::verify_quest_auth;
use crate::{models::AppState, utils::get_error};
use axum::http::HeaderMap;
use axum::{
extract::State,
http::StatusCode,
response::{IntoResponse, Json},
};
use axum_auto_routes::route;
use jsonwebtoken::{decode, Algorithm, DecodingKey, Validation};
use mongodb::bson::doc;
use mongodb::options::FindOneOptions;
use serde::Deserialize;
use serde_json::json;
use starknet::core::types::FieldElement;
use std::str::FromStr;
use std::sync::Arc;

pub_struct!(Deserialize; CreateBalance {
quest_id: i64,
name: String,
desc: String,
contracts: String,
href: String,
cta: String,
});

#[route(
post,
"/admin/tasks/balance/create",
crate::endpoints::admin::balance::create_balance
)]
pub async fn handler(
State(state): State<Arc<AppState>>,
headers: HeaderMap,
body: Json<CreateBalance>,
) -> impl IntoResponse {
let user = check_authorization!(headers, &state.conf.auth.secret_key.as_ref()) as String;
let collection = state.db.collection::<QuestTaskDocument>("tasks");
// Get the last id in increasing order
let last_id_filter = doc! {};
let options = FindOneOptions::builder().sort(doc! {"id": -1}).build();
let last_doc = &collection.find_one(last_id_filter, options).await.unwrap();

let quests_collection = state.db.collection::<QuestDocument>("quests");

let res = verify_quest_auth(user, &quests_collection, &(body.quest_id as i64)).await;
if !res {
return get_error("Error creating task".to_string());
};

let mut next_id = 1;
if let Some(doc) = last_doc {
let last_id = doc.id;
next_id = last_id + 1;
}

// Build a vector of FieldElement from the comma separated contracts string
let parsed_contracts: Vec<FieldElement> = body
.contracts
.split(",")
.map(|x| FieldElement::from_str(&x).unwrap())
.collect();

let new_document = QuestTaskDocument {
name: body.name.clone(),
desc: body.desc.clone(),
verify_redirect: None,
href: body.href.clone(),
quest_id: body.quest_id,
id: next_id,
cta: body.cta.clone(),
verify_endpoint: "/quests/verify_balance".to_string(),
verify_endpoint_type: "default".to_string(),
task_type: Some("balance".to_string()),
discord_guild_id: None,
quiz_name: None,
contracts: Some(parsed_contracts),
};

// insert document to boost collection
return match collection.insert_one(new_document, None).await {
Ok(_) => (
StatusCode::OK,
Json(json!({"message": "Task created successfully"})).into_response(),
)
.into_response(),
Err(_e) => get_error("Error creating tasks".to_string()),
};
}
2 changes: 2 additions & 0 deletions src/endpoints/admin/balance/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
pub mod create_balance;
pub mod update_balance;
94 changes: 94 additions & 0 deletions src/endpoints/admin/balance/update_balance.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
use crate::models::{JWTClaims, QuestTaskDocument};
use crate::utils::verify_task_auth;
use crate::{models::AppState, utils::get_error};
use axum::http::HeaderMap;
use axum::{
extract::State,
http::StatusCode,
response::{IntoResponse, Json},
};
use axum_auto_routes::route;
use jsonwebtoken::{decode, Algorithm, DecodingKey, Validation};
use mongodb::bson::doc;
use serde::Deserialize;
use serde_json::json;
use starknet::core::types::FieldElement;
use std::str::FromStr;
use std::sync::Arc;

pub_struct!(Deserialize; CreateBalance {
id: i64,
name: Option<String>,
desc: Option<String>,
contracts: Option<String>,
href: Option<String>,
cta: Option<String>,
});

// Helper function to convert FieldElement to Bson
fn field_element_to_bson(fe: &FieldElement) -> mongodb::bson::Bson {
mongodb::bson::Bson::String(fe.to_string())
}

#[route(
post,
"/admin/tasks/balance/update",
crate::endpoints::admin::balance::update_balance
)]
pub async fn handler(
State(state): State<Arc<AppState>>,
headers: HeaderMap,
body: Json<CreateBalance>,
) -> impl IntoResponse {
let user = check_authorization!(headers, &state.conf.auth.secret_key.as_ref()) as String;
let collection = state.db.collection::<QuestTaskDocument>("tasks");

let res = verify_task_auth(user, &collection, &(body.id as i32)).await;
if !res {
return get_error("Error updating tasks".to_string());
}

// filter to get existing quest
let filter = doc! {
"id": &body.id,
};

let mut update_doc = doc! {};

if let Some(name) = &body.name {
update_doc.insert("name", name);
}
if let Some(desc) = &body.desc {
update_doc.insert("desc", desc);
}
if let Some(href) = &body.href {
update_doc.insert("href", href);
}
if let Some(cta) = &body.cta {
update_doc.insert("cta", cta);
}
if let Some(contracts) = &body.contracts {
let parsed_contracts: Vec<FieldElement> = contracts
.split(",")
.map(|x| FieldElement::from_str(&x).unwrap())
.collect();
let contracts_bson: Vec<mongodb::bson::Bson> =
parsed_contracts.iter().map(field_element_to_bson).collect();
update_doc.insert("contracts", contracts_bson);
}

// update quest query
let update = doc! {
"$set": update_doc
};

// insert document to boost collection
return match collection.find_one_and_update(filter, update, None).await {
Ok(_) => (
StatusCode::OK,
Json(json!({"message": "Task updated successfully"})).into_response(),
)
.into_response(),
Err(_e) => get_error("Error updating tasks".to_string()),
};
}
30 changes: 14 additions & 16 deletions src/endpoints/admin/custom/create_custom.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,19 @@
use crate::models::{QuestDocument, QuestTaskDocument,JWTClaims};
use crate::models::{JWTClaims, QuestDocument, QuestTaskDocument};
use crate::utils::verify_quest_auth;
use crate::{models::AppState, utils::get_error};
use axum::http::HeaderMap;
use axum::{
extract::State,
http::StatusCode,
response::{IntoResponse, Json},
};
use axum_auto_routes::route;
use mongodb::bson::{doc};
use jsonwebtoken::{decode, Algorithm, DecodingKey, Validation};
use mongodb::bson::doc;
use mongodb::options::FindOneOptions;
use serde::Deserialize;
use serde_json::json;
use std::sync::Arc;
use crate::utils::verify_quest_auth;
use axum::http::HeaderMap;
use jsonwebtoken::{Validation,Algorithm,decode,DecodingKey};


pub_struct!(Deserialize; CreateCustom {
quest_id: i64,
Expand All @@ -25,7 +24,11 @@ pub_struct!(Deserialize; CreateCustom {
api: String,
});

#[route(post, "/admin/tasks/custom/create", crate::endpoints::admin::custom::create_custom)]
#[route(
post,
"/admin/tasks/custom/create",
crate::endpoints::admin::custom::create_custom
)]
pub async fn handler(
State(state): State<Arc<AppState>>,
headers: HeaderMap,
Expand All @@ -40,8 +43,7 @@ pub async fn handler(

let quests_collection = state.db.collection::<QuestDocument>("quests");


let res= verify_quest_auth(user, &quests_collection, &(body.quest_id as i64)).await;
let res = verify_quest_auth(user, &quests_collection, &(body.quest_id as i64)).await;
if !res {
return get_error("Error creating task".to_string());
};
Expand All @@ -57,23 +59,19 @@ pub async fn handler(
desc: body.desc.clone(),
verify_redirect: Some(body.href.clone()),
href: body.href.clone(),
quest_id : body.quest_id,
quest_id: body.quest_id,
id: next_id,
cta: body.cta.clone(),
verify_endpoint: body.api.clone(),
verify_endpoint_type: "default".to_string(),
task_type: Some("custom".to_string()),
discord_guild_id: None,
quiz_name: None,
contracts: None,
};

// insert document to boost collection
return match collection
.insert_one(new_document,
None,
)
.await
{
return match collection.insert_one(new_document, None).await {
Ok(_) => (
StatusCode::OK,
Json(json!({"message": "Task created successfully"})).into_response(),
Expand Down
31 changes: 14 additions & 17 deletions src/endpoints/admin/discord/create_discord.rs
Original file line number Diff line number Diff line change
@@ -1,21 +1,19 @@
use crate::models::{QuestDocument, QuestTaskDocument,JWTClaims};
use crate::models::{JWTClaims, QuestDocument, QuestTaskDocument};
use crate::utils::verify_quest_auth;
use crate::{models::AppState, utils::get_error};
use axum::http::HeaderMap;
use axum::{
extract::State,
http::StatusCode,
response::{IntoResponse, Json},
};
use axum_auto_routes::route;
use mongodb::bson::{doc};
use jsonwebtoken::{decode, Algorithm, DecodingKey, Validation};
use mongodb::bson::doc;
use mongodb::options::FindOneOptions;
use serde::Deserialize;
use serde_json::json;
use std::sync::Arc;
use axum::http::HeaderMap;
use crate::utils::verify_quest_auth;
use jsonwebtoken::{Validation,Algorithm,decode,DecodingKey};



pub_struct!(Deserialize; CreateCustom {
quest_id: i64,
Expand All @@ -25,13 +23,17 @@ pub_struct!(Deserialize; CreateCustom {
guild_id: String,
});

#[route(post, "/admin/tasks/discord/create", crate::endpoints::admin::discord::create_discord)]
#[route(
post,
"/admin/tasks/discord/create",
crate::endpoints::admin::discord::create_discord
)]
pub async fn handler(
State(state): State<Arc<AppState>>,
headers: HeaderMap,
body: Json<CreateCustom>,
) -> impl IntoResponse {
let user = check_authorization!(headers, &state.conf.auth.secret_key.as_ref()) as String;
let user = check_authorization!(headers, &state.conf.auth.secret_key.as_ref()) as String;
let collection = state.db.collection::<QuestTaskDocument>("tasks");
// Get the last id in increasing order
let last_id_filter = doc! {};
Expand All @@ -40,8 +42,7 @@ pub async fn handler(

let quests_collection = state.db.collection::<QuestDocument>("quests");


let res= verify_quest_auth(user, &quests_collection, &(body.quest_id as i64)).await;
let res = verify_quest_auth(user, &quests_collection, &(body.quest_id as i64)).await;
if !res {
return get_error("Error creating task".to_string());
};
Expand All @@ -65,15 +66,11 @@ pub async fn handler(
discord_guild_id: Some(body.guild_id.clone()),
quiz_name: None,
verify_redirect: None,
contracts: None,
};

// insert document to boost collection
return match collection
.insert_one(new_document,
None,
)
.await
{
return match collection.insert_one(new_document, None).await {
Ok(_) => (
StatusCode::OK,
Json(json!({"message": "Task created successfully"})).into_response(),
Expand Down
Loading

0 comments on commit 903c865

Please sign in to comment.