diff --git a/backend/Cargo.toml b/backend/Cargo.toml index 8e9b54f..af25587 100644 --- a/backend/Cargo.toml +++ b/backend/Cargo.toml @@ -15,4 +15,5 @@ jwt-compact = { version = "0.8.0", features = ["ed25519-dalek"] } ed25519-dalek = "2.1.1" rand = "0.8.5" chrono = "0.4" -regex = "1.11.1" \ No newline at end of file +regex = "1.11.1" +bcrypt = "0.16" diff --git a/backend/src/auth/jwt.rs b/backend/src/auth/jwt.rs index d898a6c..5138e31 100644 --- a/backend/src/auth/jwt.rs +++ b/backend/src/auth/jwt.rs @@ -10,8 +10,7 @@ use rand::{SeedableRng, RngCore}; #[derive(Debug, Serialize, Deserialize, Clone)] pub struct CustomClaims { - pub user_id: String, - pub device_ua: String, + pub name: String, } pub enum SecretKey { diff --git a/backend/src/database/relational/builder.rs b/backend/src/database/relational/builder.rs index a9f6e13..3e0d03d 100644 --- a/backend/src/database/relational/builder.rs +++ b/backend/src/database/relational/builder.rs @@ -276,4 +276,28 @@ impl QueryBuilder { Ok((sql, values)) } + pub fn fields(mut self, fields: Vec) -> Self { + self.fields = fields; + self + } + + pub fn params(mut self, params: HashMap) -> Self { + self.params = params; + self + } + + pub fn where_clause(mut self, clause: WhereClause) -> Self { + self.where_clause = Some(clause); + self + } + + pub fn order_by(mut self, order: ValidatedValue) -> Self { + self.order_by = Some(order); + self + } + + pub fn limit(mut self, limit: i32) -> Self { + self.limit = Some(limit); + self + } } \ No newline at end of file diff --git a/backend/src/database/relational/mod.rs b/backend/src/database/relational/mod.rs index be46e9b..62dacd6 100644 --- a/backend/src/database/relational/mod.rs +++ b/backend/src/database/relational/mod.rs @@ -31,7 +31,7 @@ impl Error for DatabaseError {} #[async_trait] pub trait DatabaseTrait: Send + Sync { - async fn connect(database: config::SqlConfig) -> Result> + async fn connect(database: &config::SqlConfig) -> Result> where Self: Sized; async fn execute_query<'a>( @@ -53,7 +53,7 @@ impl Database { &self.db } - pub async fn link(database: config::SqlConfig) -> Result> { + pub async fn link(database: &config::SqlConfig) -> Result> { let db = match database.db_type.as_str() { "postgresql" => postgresql::Postgresql::connect(database).await?, _ => return Err("unknown database type".into()), diff --git a/backend/src/database/relational/postgresql/mod.rs b/backend/src/database/relational/postgresql/mod.rs index 29b18fe..566775b 100644 --- a/backend/src/database/relational/postgresql/mod.rs +++ b/backend/src/database/relational/postgresql/mod.rs @@ -34,7 +34,7 @@ impl DatabaseTrait for Postgresql { Ok(()) } - async fn connect(db_config: config::SqlConfig) -> Result> { + async fn connect(db_config: &config::SqlConfig) -> Result> { let connection_str = format!( "postgres://{}:{}@{}:{}/{}", db_config.user, db_config.password, db_config.address, db_config.port, db_config.db_name @@ -52,9 +52,9 @@ impl DatabaseTrait for Postgresql { builder: &builder::QueryBuilder, ) -> Result>, Box> { let (query, values) = builder.build()?; - + let mut sqlx_query = sqlx::query(&query); - + for value in values { sqlx_query = sqlx_query.bind(value); } diff --git a/backend/src/database/relational/uilts.rs b/backend/src/database/relational/uilts.rs new file mode 100644 index 0000000..e69de29 diff --git a/backend/src/main.rs b/backend/src/main.rs index e8464d3..c21fa63 100644 --- a/backend/src/main.rs +++ b/backend/src/main.rs @@ -49,7 +49,7 @@ impl AppState { } async fn link_sql(&self, config: config::SqlConfig) -> AppResult<()> { - let database = relational::Database::link(config) + let database = relational::Database::link(&config) .await .map_err(|e| AppError::Database(e.to_string()))?; *self.db.lock().await = Some(database); @@ -63,8 +63,7 @@ impl AppState { #[get("/system")] async fn token_system(_state: &State) -> Result, status::Custom> { let claims = auth::jwt::CustomClaims { - user_id: "system".into(), - device_ua: "system".into(), + name: "system".into(), }; auth::jwt::generate_jwt(claims, Duration::seconds(1)) @@ -73,6 +72,7 @@ async fn token_system(_state: &State) -> Result } + #[launch] async fn rocket() -> _ { let config = config::Config::read().expect("Failed to read config"); @@ -94,11 +94,28 @@ async fn rocket() -> _ { if ! config.info.install { rocket_builder = rocket_builder - .mount("/", rocket::routes![routes::install]); + .mount("/", rocket::routes![routes::intsall::install]); } rocket_builder = rocket_builder - .mount("/auth/token", routes![token_system]); + .mount("/auth/token", rocket::routes![token_system]) + .mount("/", rocket::routes![routes::intsall::test]); rocket_builder -} \ No newline at end of file +} + +#[tokio::test] +async fn test_placeholder() { + let config = config::Config::read().expect("Failed to read config"); + + let state = AppState { + db: Arc::new(Mutex::new(None)), + configure: Arc::new(Mutex::new(config.clone())), + }; + state.link_sql(config.sql_config.clone()) + .await + .expect("Failed to connect to database"); + let sql=state.get_sql().await.expect("Failed to get sql"); + let _=routes::person::insert(&sql,routes::person::RegisterData{ name: String::from("test"), email: String::from("lsy22@vip.qq.com"), password:String::from("test") }).await.unwrap(); +} + diff --git a/backend/src/routes/intsall.rs b/backend/src/routes/intsall.rs index 65ad73b..67143a1 100644 --- a/backend/src/routes/intsall.rs +++ b/backend/src/routes/intsall.rs @@ -1,71 +1,93 @@ use serde::{Deserialize,Serialize}; use crate::{config,utils}; -use crate::database::{relational,relational::builder}; +use crate::database::relational; use crate::{AppState,AppError,AppResult}; use rocket::{ - get, post, + post, http::Status, response::status, serde::json::Json, State, }; -use std::collections::HashMap; +use crate::routes::person; +use crate::auth; +use chrono::Duration; #[derive(Deserialize, Serialize)] -struct InstallData{ +pub struct InstallData{ name:String, email:String, password:String, sql_config: config::SqlConfig - } #[derive(Deserialize, Serialize)] -struct InstallReplyData{ +pub struct InstallReplyData{ token:String, name:String, password:String, } - -#[post("/install", format = "application/json", data = "")] -async fn install( +#[post("/test", format = "application/json", data = "")] +pub async fn test( data: Json, state: &State -) -> AppResult>, AppError> { +) -> Result, status::Custom> { + let data=data.into_inner(); + + + let sql= state.get_sql().await.map_err(|e| e)?; + + let _ = person::insert(&sql,person::RegisterData{ name: data.name.clone(), email: data.email, password:data.password }); + Ok(status::Custom(Status::Ok, "Installation successful".to_string())) + +} + +#[post("/install", format = "application/json", data = "")] +pub async fn install( + data: Json, + state: &State +) -> Result>, status::Custom> { let mut config = state.configure.lock().await; if config.info.install { - return Err(AppError::Database("Database already initialized".to_string())); + return Err(status::Custom(Status::BadRequest, "Database already initialized".to_string())); } let data=data.into_inner(); relational::Database::initial_setup(data.sql_config.clone()) .await - .map_err(|e| AppError::Database(e.to_string()))?; + .map_err(|e| status::Custom(Status::InternalServerError, e.to_string()))?; config.info.install = true; - state.link_sql(data.sql_config.clone()); - let sql= state.get_sql() - .await? - .get_db(); + state.link_sql(data.sql_config.clone()).await?; + let sql= state.get_sql().await?; let system_name=utils::generate_random_string(20); let system_password=utils::generate_random_string(20); - let mut builder = builder::QueryBuilder::new(builder::SqlOperation::Insert,String::from("persons"))?; - - let user_params=HashMap::new(); - user_params.insert("person_name", data.name); - user_params.insert("person_email", data.email); - user_params.insert("person_password", data.password); - user_params.insert("person_role", data.name); + let _ = person::insert(&sql,person::RegisterData{ name: data.name.clone(), email: data.email, password:data.password }).await?; + let _ = person::insert(&sql,person::RegisterData{ name: system_name.clone(), email: String::from("author@lsy22.com"), password:system_name.clone() }).await?; + let token = auth::jwt::generate_jwt( + auth::jwt::CustomClaims{name:data.name.clone()}, + Duration::days(7) + ).map_err(|e| status::Custom(Status::Unauthorized, e.to_string()))?; config::Config::write(config.clone()) - .map_err(|e| AppError::Config(e.to_string()))?; - Ok() + .map_err(|e| status::Custom(Status::InternalServerError, e.to_string()))?; + Ok( + status::Custom( + Status::Ok, + Json(InstallReplyData{ + token:token, + name: system_name, + password: system_password + } + ) + ) + ) } \ No newline at end of file diff --git a/backend/src/routes/mod.rs b/backend/src/routes/mod.rs index d661ca0..9d3a597 100644 --- a/backend/src/routes/mod.rs +++ b/backend/src/routes/mod.rs @@ -1,6 +1,8 @@ -mod intsall; -use rocket::routes; +pub mod intsall; +pub mod person; +use rocket::routes; + +// pub fn create_routes() -> Vec { + +// } -pub fn create_routes() -> routes { - routes!["/", intsall::install] -} \ No newline at end of file diff --git a/backend/src/routes/person.rs b/backend/src/routes/person.rs new file mode 100644 index 0000000..42afe5f --- /dev/null +++ b/backend/src/routes/person.rs @@ -0,0 +1,68 @@ +use serde::{Deserialize,Serialize}; +use crate::{config,utils}; +use crate::database::{relational,relational::builder}; +use crate::{AppError,AppResult}; +use rocket::{ + get, post, + http::Status, + response::status, + serde::json::Json, + State, +}; +use std::collections::HashMap; +use bcrypt::{hash, verify, DEFAULT_COST}; + + +#[derive(Deserialize, Serialize)] +pub struct LoginData{ + pub name:String, + pub password:String +} + +pub struct RegisterData{ + pub name:String, + pub email:String, + pub password:String +} + +pub async fn insert(sql:&relational::Database,data:RegisterData) -> AppResult<()>{ + let hashed_password = hash(data.password, DEFAULT_COST).expect("Failed to hash password"); + + + let mut user_params=HashMap::new(); + user_params.insert( + builder::ValidatedValue::Identifier(String::from("person_name")) + , + builder::ValidatedValue::PlainText(data.name) + ); + user_params.insert( + builder::ValidatedValue::Identifier(String::from("person_email")) + , + builder::ValidatedValue::PlainText(data.email) + ); + user_params.insert( + builder::ValidatedValue::Identifier(String::from("person_password")) + , + builder::ValidatedValue::PlainText(hashed_password) + ); + + let builder = builder::QueryBuilder::new(builder::SqlOperation::Insert,String::from("persons")) + .map_err(|e|{ + AppError::Database(format!("Error while building query: {}", e.to_string())) + })? + .params(user_params) + ; + + let _= sql.get_db().execute_query(&builder).await.map_err(|e|{ + AppError::Database(format!("Travel during execution: {}", e.to_string())) + })?; + Ok(()) +} + +pub fn delete(){} + +pub fn update(){} + +pub fn select(){} + +pub fn check(){} \ No newline at end of file