From 80cb8e12c66fb968d749f4e3fc4e20725c44a934 Mon Sep 17 00:00:00 2001 From: iltifatibad Date: Tue, 7 Jan 2025 23:35:18 +0000 Subject: [PATCH 1/4] Detailed Api Examples --- actix-web/detailed-api-examples/.gitignore | 3 + actix-web/detailed-api-examples/Cargo.toml | 28 ++ actix-web/detailed-api-examples/README.md | 97 +++++ actix-web/detailed-api-examples/database.db | Bin 0 -> 45056 bytes actix-web/detailed-api-examples/src/app.rs | 421 +++++++++++++++++++ actix-web/detailed-api-examples/src/main.rs | 48 +++ actix-web/detailed-api-examples/src/token.rs | 86 ++++ 7 files changed, 683 insertions(+) create mode 100644 actix-web/detailed-api-examples/.gitignore create mode 100644 actix-web/detailed-api-examples/Cargo.toml create mode 100644 actix-web/detailed-api-examples/README.md create mode 100644 actix-web/detailed-api-examples/database.db create mode 100644 actix-web/detailed-api-examples/src/app.rs create mode 100644 actix-web/detailed-api-examples/src/main.rs create mode 100644 actix-web/detailed-api-examples/src/token.rs diff --git a/actix-web/detailed-api-examples/.gitignore b/actix-web/detailed-api-examples/.gitignore new file mode 100644 index 00000000..cfcbc42b --- /dev/null +++ b/actix-web/detailed-api-examples/.gitignore @@ -0,0 +1,3 @@ +/target +.shuttle* +Secrets*.toml diff --git a/actix-web/detailed-api-examples/Cargo.toml b/actix-web/detailed-api-examples/Cargo.toml new file mode 100644 index 00000000..303e225e --- /dev/null +++ b/actix-web/detailed-api-examples/Cargo.toml @@ -0,0 +1,28 @@ +[package] +name = "detailed-api-examples" +version = "0.1.0" +edition = "2021" + +[dependencies] +actix-web = "4.1.0" +actix-files = "*" +dotenv = "0.15.0" +env_logger = "0.9.0" +log = "0.4.17" +tera = "1.16.0" +serde = {version = "*", features = ["derive"]} +actix-session = {version="0.6.2", features= ["cookie-session"]} +actix-redis = "0.11.0" +validator = { version = "0.15", features = ["derive"] } +bcrypt = "0.13.0" +actix-cors = "0.7.0" +rand = "0.8.5" +actix-multipart = "0.6.1" +futures-util = "0.3" # next() için gerekli +sha2 = "0.10.8" +chrono = "0.4.39" +base64 = "0.22.1" +shuttle-actix-web = "0.49.0" +shuttle-runtime = "0.49.0" +tokio = "1.26.0" +libsql = "0.6.0" diff --git a/actix-web/detailed-api-examples/README.md b/actix-web/detailed-api-examples/README.md new file mode 100644 index 00000000..39683672 --- /dev/null +++ b/actix-web/detailed-api-examples/README.md @@ -0,0 +1,97 @@ +About the Project + + + +This project is an API server developed using the Rust programming language. It leverages the Actix Web framework + +for handling HTTP-based requests and integrates with the Turso database for performing various data operations. + + + +Technologies Used + +- Actix Web: A fast and powerful web framework. + +- Actix Session: Used for session management. + +- Turso Database: A fast and remote database solution. + +- dotenv: For managing environment variables easily. + +- Shuttle Runtime: A cloud solution for quickly deploying and running the project. + + + +API Endpoints + +| Method | Endpoint | Description | + +|--------|--------------------|----------------------------| + +| POST | /api/user/create | Creates a new user. | + +| POST | /api/user/login | Handles user login. | + +| POST | /api/news/upload | Uploads news. | + +| POST | /api/events/upload | Uploads events. | + +| GET | /api/get | Fetches data. | + +| GET | /api/get/a | Fetches statistics. | + +| POST | /api/check/token | Verifies a token. | + + + +Setting Up the Development Environment + + + +1. Clone the Repository: + + git clone https://github.com/your-username/your-repository.git + + cd your-repository + + + +2. Install Required Dependencies: + + Use Cargo to install the dependencies: + + cargo build + + + +3. Configure Environment Variables: + + Add the following keys to your Secrets.toml file: + + TURSO_DATABASE_URL= + + TURSO_AUTH_TOKEN= + + + +4. Run the Project: + + cargo run + + + +Deploying the Project with Shuttle + + + +1. Install Shuttle CLI: + + cargo install cargo-shuttle + + + +2. Deploy the Project: + + cargo shuttle deploy + +""" diff --git a/actix-web/detailed-api-examples/database.db b/actix-web/detailed-api-examples/database.db new file mode 100644 index 0000000000000000000000000000000000000000..579000cd67abe2ca35d9fdd41ae9bed3a32c8b46 GIT binary patch literal 45056 zcmeI(L2J`s9LMoQXR~Ie-3}w{AUva>Rz(=YlZZ|kA`FIeUc8iSeVk!UvL#R4iXe15 zi!Z{9Z$@ypy%h0ek~!Liq{V}m=398W>vw?RBxe z_IzVq4C~^q>2`(dD{&CIo)h-PzU&J>P{NOUy$5C-$#6G_lm7IYVciRS*)QtwKQ~UsJ)p^z+3~?j`dfL*?d2m!x+L!)~?o(|VTvf+@W?Ln zcHS9@(^(lgsW*OVTVvP#KX(pp&l~pAl74b$1pDG6YX!uUR-5bX9XRSke!7cnyE59% zUst{?+V>Y~_S2>DVwYG()6!;Biq%HVez{!sqTzSGvobuMYyQ%1l{yYY)36s8^^;cS zm43I=Hgm-)t>mQgzasw)O0?T%&3q*Sa$x8FimxiVT0R#|0009IL zKmY**5I_I{1g1d1Ofzu*KLs;M1OyO3009ILKmY**5I_I{1j+^2|1Y, + pub limit: Option, +} + + +#[derive(Debug, Serialize, Deserialize)] +struct Statistics { + total_income: i32, + users_today: i32, + news_today: i32, + events_today: i32, +} + +#[derive(Serialize)] +struct User { + id: i32, + name: String, + surname: String, + email: String, + password: String, + token: String, +} + +#[derive(Deserialize)] +pub struct Info { + path : String, + id : Option +} + +#[derive(Serialize)] +struct NewsItem { + id: i32, + imgpath: String, + name: String, + description: String, +} + +#[derive(Serialize)] +struct NewsItemLanding { + imgpath: String, + name: String, +} + +pub async fn create_user( + user: web::Json, + db: web::Data, +) -> Result { + let mut ranid: i32 = rand::thread_rng().gen(); + let conn = db.connect().expect("Hello"); + + // Check if user with generated ID already exists + let query_result = conn.query("SELECT * FROM users WHERE id = ?", libsql::params![ranid]).await; + if query_result.is_ok() { + ranid = rand::thread_rng().gen(); + } + + let token: String = generate_token(&ranid, "user").await; + let registertime = Utc::now().to_string(); + + let result = conn.execute( + "INSERT INTO users (id, name, surname, email, password, number, duty, token, registertime) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)", + libsql::params![ + ranid, + user.name.clone(), + user.surname.clone(), + user.email.clone(), + user.password.clone(), + user.phone, + "user", + token, + registertime + ], + ).await; + + match result { + Ok(_) => Ok(HttpResponse::Ok().json("User successfully registered")), + Err(e) => { + eprintln!("Error occurred while inserting user: {:?}", e); + Ok(HttpResponse::InternalServerError().json(format!("Error: {}", e))) + }, + } +} + +pub async fn login_user( + user: web::Json, + db: web::Data, +) -> Result { + let conn = db.connect().expect("A"); + + // Query the database for the user + let query_result = conn.query( + "SELECT id, name, surname, email, password, token FROM users WHERE email = ? AND password = ?", + libsql::params![user.email.clone(), user.password.clone()] + ).await; + + match query_result { + Ok(mut rows) => { + if let Some(_) = rows.next().await.expect("B") { + Ok(HttpResponse::Ok().json("Ok")) + } else { + Ok(HttpResponse::BadRequest().json("Not Ok")) + } + }, + Err(_) => Ok(HttpResponse::BadRequest().json("Not Ok")) + } +} +pub async fn news_upload(mut payload: Multipart, db: web::Data) -> Result { + let mut name = String::new(); + let mut description = String::new(); + let mut imgpath = String::new(); + + let mut ranid: i32 = rand::thread_rng().gen(); + let conn = db.connect().expect("C"); + + // Check if news with generated ID already exists + let query_result = conn.query("SELECT * FROM news WHERE id = ?", libsql::params![ranid]).await; + if query_result.is_ok() { + ranid = rand::thread_rng().gen(); + } + + // Process multipart form data + while let Some(item) = payload.next().await { + let mut field = item?; + let field_name = field.name().to_string(); + match field_name.as_str() { + "file" => { + if let Some(filename) = field.content_disposition().get_filename() { + imgpath = format!("../Front-End/public/newsupload/{}", filename); + let mut f = File::create(&imgpath)?; + + while let Some(chunk) = field.next().await { + let data = chunk?; + f.write_all(&data)?; + } + println!("Image Saved: {}", imgpath); + } + } + "name" => { + let mut value = Vec::new(); + while let Some(chunk) = field.next().await { + value.extend_from_slice(&chunk?); + } + name = String::from_utf8(value).unwrap_or_default(); + } + "description" => { + let mut value = Vec::new(); + while let Some(chunk) = field.next().await { + value.extend_from_slice(&chunk?); + } + description = String::from_utf8(value).unwrap_or_default(); + } + _ => {} + } + } + + imgpath = imgpath.replace("../Front-End", ""); + let registertime = Utc::now().to_string(); + + let result = conn.execute( + "INSERT INTO news (id, imgpath, name, desc, registertime) VALUES (?, ?, ?, ?, ?)", + libsql::params![ranid, imgpath, name, description, registertime] + ).await; + + match result { + Ok(_) => Ok(HttpResponse::Ok().json("News successfully registered")), + Err(e) => { + eprintln!("Error occurred while inserting user: {:?}", e); + Ok(HttpResponse::InternalServerError().json(format!("Error: {}", e))) + }, + } + +} + +pub async fn events_upload(mut payload: Multipart, db: web::Data) -> Result { + let mut name = String::new(); + let mut description = String::new(); + let mut imgpath = String::new(); + + let mut ranid: i32 = rand::thread_rng().gen(); + let conn = db.connect().expect("D"); + + // Check if event with generated ID already exists + let query_result = conn.query("SELECT * FROM events WHERE id = ?", libsql::params![ranid]).await; + if query_result.is_ok() { + ranid = rand::thread_rng().gen(); + } + + // Process multipart form data + while let Some(item) = payload.next().await { + let mut field = item?; + let field_name = field.name().to_string(); + match field_name.as_str() { + "file" => { + if let Some(filename) = field.content_disposition().get_filename() { + imgpath = format!("../Front-End/public/eventsupload/{}", filename); + let mut f = File::create(&imgpath)?; + + while let Some(chunk) = field.next().await { + let data = chunk?; + f.write_all(&data)?; + } + println!("Image Saved: {}", imgpath); + } + } + "name" => { + let mut value = Vec::new(); + while let Some(chunk) = field.next().await { + value.extend_from_slice(&chunk?); + } + name = String::from_utf8(value).unwrap_or_default(); + } + "description" => { + let mut value = Vec::new(); + while let Some(chunk) = field.next().await { + value.extend_from_slice(&chunk?); + } + description = String::from_utf8(value).unwrap_or_default(); + } + _ => {} + } + } + + imgpath = imgpath.replace("../Front-End", ""); + let registertime = Utc::now().to_string(); + + let result = conn.execute( + "INSERT INTO events (id, imgpath, name, desc, registertime) VALUES (?, ?, ?, ?, ?)", + libsql::params![ranid, imgpath, name, description, registertime] + ).await; + + match result { + Ok(_) => Ok(HttpResponse::Ok().json("Events successfully registered")), + Err(e) => { + eprintln!("Error occurred while inserting event: {:?}", e); + Ok(HttpResponse::InternalServerError().json(format!("Error: {}", e))) + }, + } + +} + + + +pub async fn get_data (payload: web::Query, db: web::Data) -> Result { + println!("AAAAA {}", &payload.path); + let conn = db.connect().expect(" Get Data DataBase Error ."); + if &payload.path == "api/get/news" { + let mut query = conn.query("SELECT * FROM news", ()).await.unwrap(); + + let mut news_items: Vec = Vec::new(); + while let Some(row) = query.next().await.unwrap() { + let news_item = NewsItem { + id: row.get(0).unwrap(), + imgpath: row.get(1).unwrap(), + name: row.get(2).unwrap(), + description: row.get(3).unwrap(), + }; + news_items.push(news_item); + } + return Ok(HttpResponse::Ok().json(news_items)); + } else if &payload.path == "api/get/landingnews" { + let mut query = conn.query("select * from news where id = 1?", libsql::params![&payload.id]).await.unwrap(); + + let mut news_items: Vec = Vec::new(); + while let Some(row) = query.next().await.unwrap() { + let news_item = NewsItemLanding { + imgpath: row.get(0).unwrap(), + name: row.get(1).unwrap() + }; + news_items.push(news_item); + } + return Ok(HttpResponse::Ok().json(news_items)); + + } else if &payload.path == "api/get/events" { + let mut query = conn.query("select * from events", ()).await.unwrap(); + let mut events_items: Vec = Vec::new(); + while let Some(row) = query.next().await.unwrap() { + let events_item: NewsItem = NewsItem { + id: row.get(0).unwrap(), + imgpath: row.get(1).unwrap(), + name: row.get(2).unwrap(), + description: row.get(3).unwrap(), + }; + events_items.push(events_item); + } + return Ok(HttpResponse::Ok().json(events_items)); + + } else if &payload.path == "api/get/eventslanding" { + let mut query = conn.query("select * from events where id = 1?", libsql::params![&payload.id]).await.unwrap(); + let mut news_items: Vec = Vec::new(); + while let Some(row) = query.next().await.unwrap() { + let news_item = NewsItemLanding { + imgpath: row.get(0).unwrap(), + name: row.get(1).unwrap() + }; + news_items.push(news_item); + } + return Ok(HttpResponse::Ok().json(news_items)); + + } + + else { + return Ok(HttpResponse::Ok().json("Not Correct")); + } + +} + +pub async fn get_statistics( + query_params: web::Query, + db: web::Data, +) -> Result { + let conn = db.connect().expect(" Failed To Connect Statistics Databse "); + if &query_params.path == "api/get/income" { + let mut user_count_result = conn.query("SELECT COUNT(*) as user_count from users", ()).await.expect(" Error "); + let mut total_user_count: i32 = 0; + while let Some(row) = user_count_result.next().await.unwrap(){ + total_user_count = row.get(0).unwrap(); + }; + + let total_income = total_user_count * 3; + + let mut users_today:i32 = 0; + let mut today_users_result = conn.query("SELECT COUNT(*) as user_count_today + FROM users + WHERE DATE(SUBSTR(registertime, 1, 10)) = DATE('now')", ()).await.expect(" Error 2 "); + + + while let Some(row) = today_users_result.next().await.unwrap(){ + users_today = row.get(0).unwrap(); + }; + + + let mut news_today:i32 = 0; + let mut today_news_result = conn.query("SELECT COUNT(*) as news_count_today + FROM news + WHERE DATE(SUBSTR(registertime, 1, 10)) = DATE('now')", ()).await.expect(" Error 3 "); + + while let Some(row) = today_news_result.next().await.unwrap(){ + news_today = row.get(0).unwrap(); + }; + + let mut events_today:i32 = 0; + let mut today_events_result = conn.query("SELECT COUNT(*) as events_count_today + FROM events + WHERE DATE(SUBSTR(registertime, 1, 10)) = DATE('now')", ()).await.expect(" Error 4 "); + + while let Some(row) = today_events_result.next().await.unwrap(){ + events_today = row.get(0).unwrap(); + }; + + let statistics_data = Statistics { + total_income, + users_today, + news_today, + events_today, + }; + + return Ok(HttpResponse::Ok().json(statistics_data)); + }else if query_params.path == "api/get/all/users" { + let page = query_params.page.unwrap_or(1); + let limit = query_params.limit.unwrap_or(10); + + let offset = (page - 1) * limit; + + match conn.query("SELECT id, name, surname, email, password, token FROM users LIMIT ? OFFSET ?", libsql::params![limit, offset]).await { + Ok(mut query_result) => { + let mut users = Vec::new(); + + while let Some(row) = query_result.next().await.unwrap() { + let user = User { + id: row.get(0).unwrap(), + name: row.get(1).unwrap(), + surname: row.get(2).unwrap(), + email: row.get(3).unwrap(), + password: row.get(4).unwrap(), + token: row.get(5).unwrap(), + }; + users.push(user); + } + + return Ok(HttpResponse::Ok().json(users)); + } + Err(e) => { + eprintln!("Database query error: {:?}", e); + return Ok(HttpResponse::InternalServerError().body("Failed to fetch users")); + } + } + } + else { + return Ok(HttpResponse::Ok().finish()); + } +} diff --git a/actix-web/detailed-api-examples/src/main.rs b/actix-web/detailed-api-examples/src/main.rs new file mode 100644 index 00000000..8167b1f1 --- /dev/null +++ b/actix-web/detailed-api-examples/src/main.rs @@ -0,0 +1,48 @@ +use std::env; +use std::str::FromStr; +use actix_cors::Cors; +use actix_files::Files; +use actix_session::{SessionMiddleware}; +use actix_session::storage::CookieSessionStore; +use actix_web::cookie::Key; +use actix_web::{http, web, App, HttpServer}; +use actix_web::middleware::Logger; +use dotenv::dotenv; +use token::verify_token; +use crate::app::*; +use actix_web::{get, web::{ServiceConfig}, HttpResponse, Error}; +use shuttle_actix_web::ShuttleActixWeb; +use shuttle_runtime::SecretStore; +use libsql::Builder; +mod token; +mod app; + + + +#[shuttle_runtime::main] +async fn main(#[shuttle_runtime::Secrets] secrets: SecretStore) -> ShuttleActixWeb { + dotenv().ok(); + + let db_url = secrets.get("TURSO_DATABASE_URL").unwrap(); + let auth_token = secrets.get("TURSO_AUTH_TOKEN").unwrap(); + + let db = Builder::new_remote(db_url, auth_token) + .build() + .await + .expect("Failed to connect to database"); + + let conn = web::Data::new(db); + + let config = move |cfg: &mut ServiceConfig| { + cfg.app_data(conn.clone()); + cfg.route("/api/user/create", web::post().to(create_user)); + cfg.route("/api/user/login", web::post().to(login_user)); + cfg.route("/api/news/upload", web::post().to(news_upload)); + cfg.route("/api/events/upload", web::post().to(events_upload)); + cfg.route("/api/get", web::get().to(get_data)); + cfg.route("/api/get/a", web::get().to(get_statistics)); + cfg.route("/api/check/token", web::post().to(verify_token)); + }; + + Ok(config.into()) +} diff --git a/actix-web/detailed-api-examples/src/token.rs b/actix-web/detailed-api-examples/src/token.rs new file mode 100644 index 00000000..446ec638 --- /dev/null +++ b/actix-web/detailed-api-examples/src/token.rs @@ -0,0 +1,86 @@ +use actix_web::{web, Error, HttpResponse}; +use serde::Deserialize; +use sha2::{Sha256, Digest}; +use rand::Rng; +use chrono::{Utc, Duration}; + +pub async fn generate_token(user_id: &i32, role: &str) -> String { + let secret_key = "iusearchbtw)"; + let timestamp = Utc::now().timestamp(); + let random_bytes: [u8; 16] = rand::thread_rng().gen(); + + let data = format!("{}:{}:{}:{:?}", user_id, role, timestamp, random_bytes); + + let mut hasher = Sha256::new(); + hasher.update(format!("{}{}", data, secret_key)); + let hashed_token = hasher.finalize(); + + format!("{:x}.{}.{}", hashed_token, timestamp, base64::encode(random_bytes)) +} + +#[derive(Debug, Deserialize)] +pub struct TokenStruct { + token: String, + user_id: String, + role: String, + requested_page:String +} + +pub async fn verify_token(rdata : web::Json) -> Result { + let secret_key = "iusearchbtw)"; + let expiration_limit: i64 = 21600; + + let parts: Vec<&str> = rdata.token.split('.').collect(); + if parts.len() != 3 { + return Ok(HttpResponse::BadRequest().json("Token Structure Isn't Valid")); + } + + let token_hash = parts[0]; + let timestamp: i64 = match parts[1].parse() { + Ok(ts) => ts, + Err(_) => return Ok(HttpResponse::BadRequest().json("Invalid Timestamp")), + }; + let random_bytes = match base64::decode(parts[2]) { + Ok(bytes) => bytes, + Err(_) => return Ok(HttpResponse::BadRequest().json("Encryption Isn't True")), + }; + + let data = format!("{}:{}:{}:{:?}", rdata.user_id, rdata.role, timestamp, random_bytes); + let mut hasher = Sha256::new(); + hasher.update(format!("{}{}", data, secret_key)); + let expected_hash = format!("{:x}", hasher.finalize()); + + if token_hash != expected_hash { + return Ok(HttpResponse::BadRequest().json("Invalid Token")); + } + + let current_timestamp = Utc::now().timestamp(); + if current_timestamp - timestamp > expiration_limit { + let new_token = generate_token(&rdata.user_id.parse::().unwrap_or(0), &rdata.role).await; + return Ok(HttpResponse::Ok().json(format!( + "Token Expired. New Token: {}", + new_token + ))); + } + + // Role ayırımı yap + match rdata.role.as_str() { + "admin" => { + if rdata.requested_page == "admin_page" { + Ok(HttpResponse::Ok().json("Welcome to Admin Page")) + } else { + Ok(HttpResponse::Ok().json("Page Accessible")) + } + } + "user" => { + if rdata.requested_page == "admin_page" { + Ok(HttpResponse::NotAcceptable().finish()) + } else { + Ok(HttpResponse::Ok().json("Page Accessible")) + } + } + _ => { + Ok(HttpResponse::BadRequest().json("Invalid Role")) + } + } +} \ No newline at end of file From b4be2824569ebf78c740b23b4fa970e55956d2cd Mon Sep 17 00:00:00 2001 From: iltifatibad Date: Tue, 7 Jan 2025 23:47:19 +0000 Subject: [PATCH 2/4] Detailed API Examples --- actix-web/detailed-api-examples/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/actix-web/detailed-api-examples/README.md b/actix-web/detailed-api-examples/README.md index 39683672..8d8bceac 100644 --- a/actix-web/detailed-api-examples/README.md +++ b/actix-web/detailed-api-examples/README.md @@ -50,9 +50,9 @@ Setting Up the Development Environment 1. Clone the Repository: - git clone https://github.com/your-username/your-repository.git + git clone https://github.com/shuttle-hq/shuttle-examples/detailed-api-examples/ - cd your-repository + cd shuttle-examples/detailed-api-examples/ From 4d4af8b7d88c8237e6f46d334247538c1660318e Mon Sep 17 00:00:00 2001 From: iltifatibad Date: Tue, 7 Jan 2025 23:50:18 +0000 Subject: [PATCH 3/4] Detailed API Examples --- actix-web/detailed-api-examples/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actix-web/detailed-api-examples/Cargo.toml b/actix-web/detailed-api-examples/Cargo.toml index 303e225e..a1ef016a 100644 --- a/actix-web/detailed-api-examples/Cargo.toml +++ b/actix-web/detailed-api-examples/Cargo.toml @@ -18,7 +18,7 @@ bcrypt = "0.13.0" actix-cors = "0.7.0" rand = "0.8.5" actix-multipart = "0.6.1" -futures-util = "0.3" # next() için gerekli +futures-util = "0.3" sha2 = "0.10.8" chrono = "0.4.39" base64 = "0.22.1" From 995a36c6eef6200af41effefd1198ef8d1ffe180 Mon Sep 17 00:00:00 2001 From: Iltifat <151670452+iltifatibad@users.noreply.github.com> Date: Tue, 21 Jan 2025 20:33:02 +0000 Subject: [PATCH 4/4] Update token.rs --- actix-web/detailed-api-examples/src/token.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/actix-web/detailed-api-examples/src/token.rs b/actix-web/detailed-api-examples/src/token.rs index 446ec638..2aeb378e 100644 --- a/actix-web/detailed-api-examples/src/token.rs +++ b/actix-web/detailed-api-examples/src/token.rs @@ -63,7 +63,6 @@ pub async fn verify_token(rdata : web::Json) -> Result { if rdata.requested_page == "admin_page" { @@ -83,4 +82,4 @@ pub async fn verify_token(rdata : web::Json) -> Result