后端:精简JWT声明结构,优化用户注册功能,优化查询构建器

This commit is contained in:
lsy 2024-11-21 01:04:59 +08:00
parent 8071a16510
commit 33b53b3663
10 changed files with 177 additions and 44 deletions

View File

@ -16,3 +16,4 @@ ed25519-dalek = "2.1.1"
rand = "0.8.5" rand = "0.8.5"
chrono = "0.4" chrono = "0.4"
regex = "1.11.1" regex = "1.11.1"
bcrypt = "0.16"

View File

@ -10,8 +10,7 @@ use rand::{SeedableRng, RngCore};
#[derive(Debug, Serialize, Deserialize, Clone)] #[derive(Debug, Serialize, Deserialize, Clone)]
pub struct CustomClaims { pub struct CustomClaims {
pub user_id: String, pub name: String,
pub device_ua: String,
} }
pub enum SecretKey { pub enum SecretKey {

View File

@ -276,4 +276,28 @@ impl QueryBuilder {
Ok((sql, values)) Ok((sql, values))
} }
pub fn fields(mut self, fields: Vec<ValidatedValue>) -> Self {
self.fields = fields;
self
}
pub fn params(mut self, params: HashMap<ValidatedValue, ValidatedValue>) -> 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
}
} }

View File

@ -31,7 +31,7 @@ impl Error for DatabaseError {}
#[async_trait] #[async_trait]
pub trait DatabaseTrait: Send + Sync { pub trait DatabaseTrait: Send + Sync {
async fn connect(database: config::SqlConfig) -> Result<Self, Box<dyn Error>> async fn connect(database: &config::SqlConfig) -> Result<Self, Box<dyn Error>>
where where
Self: Sized; Self: Sized;
async fn execute_query<'a>( async fn execute_query<'a>(
@ -53,7 +53,7 @@ impl Database {
&self.db &self.db
} }
pub async fn link(database: config::SqlConfig) -> Result<Self, Box<dyn Error>> { pub async fn link(database: &config::SqlConfig) -> Result<Self, Box<dyn Error>> {
let db = match database.db_type.as_str() { let db = match database.db_type.as_str() {
"postgresql" => postgresql::Postgresql::connect(database).await?, "postgresql" => postgresql::Postgresql::connect(database).await?,
_ => return Err("unknown database type".into()), _ => return Err("unknown database type".into()),

View File

@ -34,7 +34,7 @@ impl DatabaseTrait for Postgresql {
Ok(()) Ok(())
} }
async fn connect(db_config: config::SqlConfig) -> Result<Self, Box<dyn Error>> { async fn connect(db_config: &config::SqlConfig) -> Result<Self, Box<dyn Error>> {
let connection_str = format!( let connection_str = format!(
"postgres://{}:{}@{}:{}/{}", "postgres://{}:{}@{}:{}/{}",
db_config.user, db_config.password, db_config.address, db_config.port, db_config.db_name db_config.user, db_config.password, db_config.address, db_config.port, db_config.db_name

View File

View File

@ -49,7 +49,7 @@ impl AppState {
} }
async fn link_sql(&self, config: config::SqlConfig) -> AppResult<()> { async fn link_sql(&self, config: config::SqlConfig) -> AppResult<()> {
let database = relational::Database::link(config) let database = relational::Database::link(&config)
.await .await
.map_err(|e| AppError::Database(e.to_string()))?; .map_err(|e| AppError::Database(e.to_string()))?;
*self.db.lock().await = Some(database); *self.db.lock().await = Some(database);
@ -63,8 +63,7 @@ impl AppState {
#[get("/system")] #[get("/system")]
async fn token_system(_state: &State<AppState>) -> Result<status::Custom<String>, status::Custom<String>> { async fn token_system(_state: &State<AppState>) -> Result<status::Custom<String>, status::Custom<String>> {
let claims = auth::jwt::CustomClaims { let claims = auth::jwt::CustomClaims {
user_id: "system".into(), name: "system".into(),
device_ua: "system".into(),
}; };
auth::jwt::generate_jwt(claims, Duration::seconds(1)) auth::jwt::generate_jwt(claims, Duration::seconds(1))
@ -73,6 +72,7 @@ async fn token_system(_state: &State<AppState>) -> Result<status::Custom<String>
} }
#[launch] #[launch]
async fn rocket() -> _ { async fn rocket() -> _ {
let config = config::Config::read().expect("Failed to read config"); let config = config::Config::read().expect("Failed to read config");
@ -94,11 +94,28 @@ async fn rocket() -> _ {
if ! config.info.install { if ! config.info.install {
rocket_builder = rocket_builder rocket_builder = rocket_builder
.mount("/", rocket::routes![routes::install]); .mount("/", rocket::routes![routes::intsall::install]);
} }
rocket_builder = rocket_builder 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 rocket_builder
} }
#[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();
}

View File

@ -1,71 +1,93 @@
use serde::{Deserialize,Serialize}; use serde::{Deserialize,Serialize};
use crate::{config,utils}; use crate::{config,utils};
use crate::database::{relational,relational::builder}; use crate::database::relational;
use crate::{AppState,AppError,AppResult}; use crate::{AppState,AppError,AppResult};
use rocket::{ use rocket::{
get, post, post,
http::Status, http::Status,
response::status, response::status,
serde::json::Json, serde::json::Json,
State, State,
}; };
use std::collections::HashMap; use crate::routes::person;
use crate::auth;
use chrono::Duration;
#[derive(Deserialize, Serialize)] #[derive(Deserialize, Serialize)]
struct InstallData{ pub struct InstallData{
name:String, name:String,
email:String, email:String,
password:String, password:String,
sql_config: config::SqlConfig sql_config: config::SqlConfig
} }
#[derive(Deserialize, Serialize)] #[derive(Deserialize, Serialize)]
struct InstallReplyData{ pub struct InstallReplyData{
token:String, token:String,
name:String, name:String,
password:String, password:String,
} }
#[post("/test", format = "application/json", data = "<data>")]
#[post("/install", format = "application/json", data = "<data>")] pub async fn test(
async fn install(
data: Json<InstallData>, data: Json<InstallData>,
state: &State<AppState> state: &State<AppState>
) -> AppResult<status::Custom<Json<InstallReplyData>>, AppError> { ) -> Result<status::Custom<String>, status::Custom<String>> {
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 = "<data>")]
pub async fn install(
data: Json<InstallData>,
state: &State<AppState>
) -> Result<status::Custom<Json<InstallReplyData>>, status::Custom<String>> {
let mut config = state.configure.lock().await; let mut config = state.configure.lock().await;
if config.info.install { 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(); let data=data.into_inner();
relational::Database::initial_setup(data.sql_config.clone()) relational::Database::initial_setup(data.sql_config.clone())
.await .await
.map_err(|e| AppError::Database(e.to_string()))?; .map_err(|e| status::Custom(Status::InternalServerError, e.to_string()))?;
config.info.install = true; config.info.install = true;
state.link_sql(data.sql_config.clone()); state.link_sql(data.sql_config.clone()).await?;
let sql= state.get_sql() let sql= state.get_sql().await?;
.await?
.get_db();
let system_name=utils::generate_random_string(20); let system_name=utils::generate_random_string(20);
let system_password=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 _ = 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 user_params=HashMap::new(); let token = auth::jwt::generate_jwt(
user_params.insert("person_name", data.name); auth::jwt::CustomClaims{name:data.name.clone()},
user_params.insert("person_email", data.email); Duration::days(7)
user_params.insert("person_password", data.password); ).map_err(|e| status::Custom(Status::Unauthorized, e.to_string()))?;
user_params.insert("person_role", data.name);
config::Config::write(config.clone()) config::Config::write(config.clone())
.map_err(|e| AppError::Config(e.to_string()))?; .map_err(|e| status::Custom(Status::InternalServerError, e.to_string()))?;
Ok() Ok(
status::Custom(
Status::Ok,
Json(InstallReplyData{
token:token,
name: system_name,
password: system_password
}
)
)
)
} }

View File

@ -1,6 +1,8 @@
mod intsall; pub mod intsall;
pub mod person;
use rocket::routes; use rocket::routes;
pub fn create_routes() -> routes { // pub fn create_routes() -> Vec<rocket::Route> {
routes!["/", intsall::install]
} // }

View File

@ -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(){}