diff --git a/src/templates/web_server/schema/api_schema.json b/src/templates/web_server/schema/api_schema.json index 2da7c2d..b307e27 100644 --- a/src/templates/web_server/schema/api_schema.json +++ b/src/templates/web_server/schema/api_schema.json @@ -1,11 +1,17 @@ [ { - "route": "/cat", + "route": "/token", "is_route_dynamic": false, - "method": "get", - "request_body": "None", + "method": "post", + "request_body": { + "address": "string", + "block": "number", + "price": "number" + }, "response": { - "url": "string" + "address": "string", + "block": "number", + "price": "number" } } ] \ No newline at end of file diff --git a/src/templates/web_server/src/main.rs b/src/templates/web_server/src/main.rs index e29a151..56117af 100644 --- a/src/templates/web_server/src/main.rs +++ b/src/templates/web_server/src/main.rs @@ -9,53 +9,122 @@ use std::io::Write; use std::sync::Mutex; #[derive(Serialize, Deserialize, Debug, Clone)] -struct CatImage { - url: String, +struct Token { + address: String, + block: Option, + price: f64, +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +struct Database { + tokens: HashMap, +} + +impl Database { + fn new() -> Self { + Self { + tokens: HashMap::new(), + } + } + + fn insert(&mut self, token: Token) { + self.tokens.insert(token.address.clone(), token); + } + + fn get(&self, address: &str, block: Option) -> Option<&Token> { + self.tokens.get(address) + } + + fn save_to_file(&self) -> std::io::Result<()> { + let data: String = serde_json::to_string(&self)?; + let mut file: fs::File = fs::File::create("database.json")?; + file.write_all(data.as_bytes())?; + Ok(()) + } + + fn load_from_file() -> std::io::Result { + let file_content: String = fs::read_to_string("database.json")?; + let db: Database = serde_json::from_str(&file_content)?; + Ok(db) + } } struct AppState { - client: HttpClient, + db: Mutex, + http_client: HttpClient, } -async fn fetch_random_cat_image(app_state: web::Data) -> impl Responder { - let response = app_state.client.get("https://api.thecatapi.com/v1/images/search").send().await; +async fn get_token_price( + app_state: web::Data, + token: web::Json, +) -> impl Responder { + let mut db = app_state.db.lock().unwrap(); + let token_in_db = db.get(&token.address, token.block); - match response { - Ok(mut resp) => { - if resp.status().is_success() { - let cat_images: Vec = resp.json().await.unwrap(); - HttpResponse::Ok().json(cat_images[0].clone()) - } else { - HttpResponse::InternalServerError().finish() + match token_in_db { + Some(token) => HttpResponse::Ok().json(token), + None => { + let price = + fetch_price_from_etherscan(&app_state.http_client, &token.address, token.block) + .await; + match price { + Ok(price) => { + let new_token = Token { + address: token.address.clone(), + block: token.block, + price, + }; + db.insert(new_token.clone()); + let _ = db.save_to_file(); + HttpResponse::Ok().json(new_token) + } + Err(_) => HttpResponse::InternalServerError().finish(), } } - Err(_) => HttpResponse::InternalServerError().finish(), } } +async fn fetch_price_from_etherscan( + client: &HttpClient, + address: &str, + block: Option, +) -> Result { + // Agent: Solutions Architect: Testing URL Endpoint: https://api.etherscan.io/api?module=account&action=tokenbalance&contractaddress={token_address}&address={client_address}&tag={block_number}&apikey=YourApiKeyToken + // Agent: Solutions Architect: Testing URL Endpoint: https://api.etherscan.io/api?module=proxy&action=eth_getBlockByNumber&tag={block_number}&boolean=true&apikey=YourApiKeyToken + // Fetch price from etherscan API + // This is a placeholder, replace with actual API call + Ok(0.0) +} + #[actix_web::main] async fn main() -> std::io::Result<()> { - let client = HttpClient::new(); + let db = match Database::load_from_file() { + Ok(db) => db, + Err(_) => Database::new(), + }; - let data: web::Data = web::Data::new(AppState { client }); + let data: web::Data = web::Data::new(AppState { + db: Mutex::new(db), + http_client: HttpClient::new(), + }); - return HttpServer::new(move || { + HttpServer::new(move || { App::new() .wrap( Cors::permissive() .allowed_origin_fn(|origin, _req_head| { origin.as_bytes().starts_with(b"http://localhost") || origin == "null" }) - .allowed_methods(vec!["GET"]) + .allowed_methods(vec!["GET", "POST", "PUT", "DELETE"]) .allowed_headers(vec![header::AUTHORIZATION, header::ACCEPT]) .allowed_header(header::CONTENT_TYPE) .supports_credentials() .max_age(3600), ) .app_data(data.clone()) - .route("/cat", web::get().to(fetch_random_cat_image)) + .route("/token", web::post().to(get_token_price)) }) .bind("127.0.0.1:8080")? .run() - .await; -} \ No newline at end of file + .await +}