feat: tag
This commit is contained in:
67
Cargo.lock
generated
67
Cargo.lock
generated
@@ -176,6 +176,12 @@ version = "0.22.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
|
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "base64ct"
|
||||||
|
version = "1.6.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitflags"
|
name = "bitflags"
|
||||||
version = "2.6.0"
|
version = "2.6.0"
|
||||||
@@ -389,6 +395,7 @@ checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"block-buffer",
|
"block-buffer",
|
||||||
"crypto-common",
|
"crypto-common",
|
||||||
|
"subtle",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -544,6 +551,8 @@ dependencies = [
|
|||||||
"dotenvy",
|
"dotenvy",
|
||||||
"jsonwebtoken",
|
"jsonwebtoken",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
|
"pbkdf2",
|
||||||
|
"rand_core",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"tokio",
|
"tokio",
|
||||||
@@ -559,6 +568,15 @@ version = "0.3.9"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
|
checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hmac"
|
||||||
|
version = "0.12.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e"
|
||||||
|
dependencies = [
|
||||||
|
"digest",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "http"
|
name = "http"
|
||||||
version = "1.1.0"
|
version = "1.1.0"
|
||||||
@@ -871,6 +889,29 @@ dependencies = [
|
|||||||
"windows-targets 0.52.6",
|
"windows-targets 0.52.6",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "password-hash"
|
||||||
|
version = "0.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "346f04948ba92c43e8469c1ee6736c7563d71012b17d40745260fe106aac2166"
|
||||||
|
dependencies = [
|
||||||
|
"base64ct",
|
||||||
|
"rand_core",
|
||||||
|
"subtle",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pbkdf2"
|
||||||
|
version = "0.12.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2"
|
||||||
|
dependencies = [
|
||||||
|
"digest",
|
||||||
|
"hmac",
|
||||||
|
"password-hash",
|
||||||
|
"sha2",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pem"
|
name = "pem"
|
||||||
version = "3.0.4"
|
version = "3.0.4"
|
||||||
@@ -952,6 +993,15 @@ dependencies = [
|
|||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_core"
|
||||||
|
version = "0.6.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
||||||
|
dependencies = [
|
||||||
|
"getrandom",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "redox_syscall"
|
name = "redox_syscall"
|
||||||
version = "0.5.2"
|
version = "0.5.2"
|
||||||
@@ -1108,6 +1158,17 @@ dependencies = [
|
|||||||
"digest",
|
"digest",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sha2"
|
||||||
|
version = "0.10.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"cpufeatures",
|
||||||
|
"digest",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sharded-slab"
|
name = "sharded-slab"
|
||||||
version = "0.1.7"
|
version = "0.1.7"
|
||||||
@@ -1166,6 +1227,12 @@ version = "0.11.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "subtle"
|
||||||
|
version = "2.6.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "2.0.69"
|
version = "2.0.69"
|
||||||
|
|||||||
@@ -23,3 +23,5 @@ tracing = "0.1"
|
|||||||
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
|
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
|
||||||
once_cell = "1.19.0"
|
once_cell = "1.19.0"
|
||||||
axum-macros = "0.4.1"
|
axum-macros = "0.4.1"
|
||||||
|
pbkdf2 = { version = "0.12", features = ["simple"] }
|
||||||
|
rand_core ={version = "0.6", features = ["std"]}
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ CREATE TABLE "amounts" (
|
|||||||
|
|
||||||
CREATE TABLE "users" (
|
CREATE TABLE "users" (
|
||||||
"id" BIGSERIAL PRIMARY KEY,
|
"id" BIGSERIAL PRIMARY KEY,
|
||||||
"username" TEXT NOT NULL,
|
"username" TEXT NOT NULL UNIQUE,
|
||||||
"password" TEXT NOT NULL,
|
"password" TEXT NOT NULL,
|
||||||
"mail" TEXT NOT NULL,
|
"mail" TEXT NOT NULL,
|
||||||
"is_delete" BOOLEAN NOT NULL DEFAULT FALSE,
|
"is_delete" BOOLEAN NOT NULL DEFAULT FALSE,
|
||||||
|
|||||||
2
src/ledger/mod.rs
Normal file
2
src/ledger/mod.rs
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
pub mod category;
|
||||||
|
pub mod tag;
|
||||||
146
src/ledger/tag.rs
Normal file
146
src/ledger/tag.rs
Normal file
@@ -0,0 +1,146 @@
|
|||||||
|
// use std::sync::Arc;
|
||||||
|
use axum::routing::{get, post};
|
||||||
|
use axum::{
|
||||||
|
extract::{Path, State},
|
||||||
|
http::StatusCode,
|
||||||
|
Json, Router,
|
||||||
|
};
|
||||||
|
use axum_macros::debug_handler;
|
||||||
|
use diesel::prelude::*;
|
||||||
|
// use diesel::update;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
// use serde_json::to_string;
|
||||||
|
use crate::model::db_model;
|
||||||
|
use crate::model::schema;
|
||||||
|
use crate::util;
|
||||||
|
use crate::util::req::CommonResp;
|
||||||
|
use chrono::prelude::*;
|
||||||
|
use tracing::info;
|
||||||
|
use crate::middleware::auth;
|
||||||
|
use crate::middleware::auth::Claims;
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub struct CreateTagRequest {
|
||||||
|
name: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
pub struct CreateTagResponse {
|
||||||
|
id: i64,
|
||||||
|
name: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_nest_handlers() -> Router<crate::AppState> {
|
||||||
|
Router::new()
|
||||||
|
.route("/", post(create_tag).get(get_all_tags))
|
||||||
|
.route("/:id", post(update_tag).get(get_tag))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[debug_handler]
|
||||||
|
pub async fn create_tag(
|
||||||
|
State(app_state): State<crate::AppState>,
|
||||||
|
claims: Claims,
|
||||||
|
Json(payload): Json<CreateTagRequest>,
|
||||||
|
) -> Result<Json<db_model::Tag>, (StatusCode, String)> {
|
||||||
|
let uid: i64 = claims.uid.clone();
|
||||||
|
let conn = app_state
|
||||||
|
.db
|
||||||
|
.get()
|
||||||
|
.await
|
||||||
|
.map_err(util::req::internal_error)?;
|
||||||
|
let new_tag = db_model::TagForm {
|
||||||
|
name: payload.name,
|
||||||
|
uid,
|
||||||
|
};
|
||||||
|
let res = conn
|
||||||
|
.interact(move |conn| {
|
||||||
|
diesel::insert_into(schema::tags::table)
|
||||||
|
.values(&new_tag)
|
||||||
|
.returning(db_model::Tag::as_returning())
|
||||||
|
.get_result(conn)
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.map_err(util::req::internal_error)?
|
||||||
|
.map_err(util::req::internal_error)?;
|
||||||
|
Ok(Json(res))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn update_tag(
|
||||||
|
Path(id): Path<i64>,
|
||||||
|
State(app_state): State<crate::AppState>,
|
||||||
|
claims: Claims,
|
||||||
|
Json(payload): Json<CreateTagRequest>,
|
||||||
|
) -> Result<Json<CommonResp>, (StatusCode, String)> {
|
||||||
|
let uid: i64 = claims.uid.clone();
|
||||||
|
let conn = app_state
|
||||||
|
.db
|
||||||
|
.get()
|
||||||
|
.await
|
||||||
|
.map_err(util::req::internal_error)?;
|
||||||
|
let now = Utc::now().naive_utc();
|
||||||
|
let res = conn
|
||||||
|
.interact(move |conn| {
|
||||||
|
diesel::update(schema::tags::table)
|
||||||
|
.filter(schema::tags::id.eq(id))
|
||||||
|
.filter(schema::tags::uid.eq(uid))
|
||||||
|
.set((
|
||||||
|
schema::tags::name.eq(payload.name),
|
||||||
|
schema::tags::update_at.eq(now),
|
||||||
|
))
|
||||||
|
.execute(conn)
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.map_err(util::req::internal_error)?
|
||||||
|
.map_err(util::req::internal_error)?;
|
||||||
|
let resp = util::req::CommonResp { code: 0 };
|
||||||
|
Ok(Json(resp))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn get_tag(
|
||||||
|
Path(id): Path<i64>,
|
||||||
|
State(app_state): State<crate::AppState>,
|
||||||
|
claims: Claims,
|
||||||
|
) -> Result<Json<db_model::Tag>, (StatusCode, String)> {
|
||||||
|
let uid: i64 = claims.uid.clone();
|
||||||
|
let conn = app_state
|
||||||
|
.db
|
||||||
|
.get()
|
||||||
|
.await
|
||||||
|
.map_err(util::req::internal_error)?;
|
||||||
|
let res = conn
|
||||||
|
.interact(move |conn| {
|
||||||
|
schema::tags::table
|
||||||
|
.filter(schema::tags::id.eq(id))
|
||||||
|
.filter(schema::tags::uid.eq(uid))
|
||||||
|
.select(db_model::Tag::as_select())
|
||||||
|
.limit(1)
|
||||||
|
.get_result(conn)
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.map_err(util::req::internal_error)?
|
||||||
|
.map_err(util::req::internal_error)?;
|
||||||
|
Ok(Json(res))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn get_all_tags(
|
||||||
|
State(app_state): State<crate::AppState>,
|
||||||
|
claims: Claims,
|
||||||
|
) -> Result<Json<Vec<db_model::Tag>>, (StatusCode, String)> {
|
||||||
|
let uid: i64 = claims.uid.clone();
|
||||||
|
let conn = app_state
|
||||||
|
.db
|
||||||
|
.get()
|
||||||
|
.await
|
||||||
|
.map_err(util::req::internal_error)?;
|
||||||
|
let res = conn
|
||||||
|
.interact(move |conn| {
|
||||||
|
schema::tags::table
|
||||||
|
.filter(schema::tags::uid.eq(uid))
|
||||||
|
.select(db_model::Tag::as_select())
|
||||||
|
.load(conn)
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.map_err(util::req::internal_error)?
|
||||||
|
.map_err(util::req::internal_error)?;
|
||||||
|
Ok(Json(res))
|
||||||
|
}
|
||||||
50
src/main.rs
50
src/main.rs
@@ -1,3 +1,4 @@
|
|||||||
|
use std::env;
|
||||||
use axum::{
|
use axum::{
|
||||||
// http::StatusCode,
|
// http::StatusCode,
|
||||||
// routing::{get, post},
|
// routing::{get, post},
|
||||||
@@ -5,18 +6,21 @@ use axum::{
|
|||||||
Router,
|
Router,
|
||||||
};
|
};
|
||||||
use axum::http::Method;
|
use axum::http::Method;
|
||||||
use serde::{Deserialize, Serialize};
|
// use pbkdf2::password_hash::Error;
|
||||||
|
// use serde::{Deserialize, Serialize};
|
||||||
use tower::ServiceBuilder;
|
use tower::ServiceBuilder;
|
||||||
use tower_http::cors::{Any, CorsLayer};
|
use tower_http::cors::{Any, CorsLayer};
|
||||||
use tower_http::trace::TraceLayer;
|
use tower_http::trace::TraceLayer;
|
||||||
use tracing::info;
|
use tracing::info;
|
||||||
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
|
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
|
||||||
|
use crate::util::pass::get_pbkdf2_from_psw;
|
||||||
|
|
||||||
// Project modules
|
// Project modules
|
||||||
mod category;
|
mod ledger;
|
||||||
mod middleware;
|
mod middleware;
|
||||||
mod model;
|
mod model;
|
||||||
mod util;
|
mod util;
|
||||||
|
mod user;
|
||||||
|
|
||||||
// Passed App State
|
// Passed App State
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@@ -30,6 +34,12 @@ async fn main() {
|
|||||||
tracing_subscriber::registry()
|
tracing_subscriber::registry()
|
||||||
.with(tracing_subscriber::fmt::layer())
|
.with(tracing_subscriber::fmt::layer())
|
||||||
.init();
|
.init();
|
||||||
|
|
||||||
|
let args: Vec<String> = env::args().collect();
|
||||||
|
|
||||||
|
if args.len() <= 1 {
|
||||||
|
return;
|
||||||
|
}
|
||||||
// initialize db connection
|
// initialize db connection
|
||||||
let db_url = std::env::var("DATABASE_URL").unwrap();
|
let db_url = std::env::var("DATABASE_URL").unwrap();
|
||||||
|
|
||||||
@@ -39,6 +49,37 @@ async fn main() {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let shared_state = AppState { db: pool };
|
let shared_state = AppState { db: pool };
|
||||||
|
let cmd = args[1].clone();
|
||||||
|
|
||||||
|
match cmd.as_str() {
|
||||||
|
"add_user" => {
|
||||||
|
println!("adding user");
|
||||||
|
if args.len() <= 4 {
|
||||||
|
println!("insufficient arg number");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let user = args[2].clone();
|
||||||
|
let psw = args[3].clone();
|
||||||
|
let mail = args[4].clone();
|
||||||
|
println!("adding user {}", user);
|
||||||
|
let hashed = get_pbkdf2_from_psw(psw);
|
||||||
|
let mut hash_psw = "".to_string();
|
||||||
|
match hashed {
|
||||||
|
Ok(val) => {
|
||||||
|
println!("get hash {}", val);
|
||||||
|
hash_psw=val;
|
||||||
|
}
|
||||||
|
Err(_) => {}
|
||||||
|
}
|
||||||
|
let res = user::dal::add_user(shared_state, user, hash_psw, mail)
|
||||||
|
.await;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
println!("unknown command {}", cmd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Register routers
|
// Register routers
|
||||||
let cors_layer = CorsLayer::new()
|
let cors_layer = CorsLayer::new()
|
||||||
@@ -50,8 +91,9 @@ async fn main() {
|
|||||||
|
|
||||||
let app = Router::new()
|
let app = Router::new()
|
||||||
// V1 apis
|
// V1 apis
|
||||||
.nest("/api/v1/category", category::handler::get_nest_handlers())
|
.nest("/api/v1/category", ledger::category::get_nest_handlers())
|
||||||
.nest("/api/v1/v2", category::handler::get_nest_handlers())
|
.nest("/api/v1/tag", ledger::tag::get_nest_handlers())
|
||||||
|
.nest("/api/v1/user", user::handler::get_nest_handlers())
|
||||||
.with_state(shared_state)
|
.with_state(shared_state)
|
||||||
.layer(global_layer);
|
.layer(global_layer);
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ use crate::util;
|
|||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
pub struct Claims {
|
pub struct Claims {
|
||||||
sub: String,
|
sub: String,
|
||||||
company: String,
|
// company: String,
|
||||||
exp: usize,
|
exp: usize,
|
||||||
pub uid: i64,
|
pub uid: i64,
|
||||||
}
|
}
|
||||||
@@ -68,7 +68,7 @@ impl Keys {
|
|||||||
|
|
||||||
impl Display for Claims {
|
impl Display for Claims {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
write!(f, "Email: {}\nCompany: {}", self.sub, self.company)
|
write!(f, "Email: {}", self.sub)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,3 +19,40 @@ pub struct CategoryForm {
|
|||||||
pub uid: i64,
|
pub uid: i64,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Queryable, Selectable, serde::Serialize, serde::Deserialize)]
|
||||||
|
#[diesel(table_name = schema::tags)]
|
||||||
|
#[diesel(check_for_backend(diesel::pg::Pg))]
|
||||||
|
pub struct Tag {
|
||||||
|
id: i64,
|
||||||
|
uid: i64,
|
||||||
|
name: String,
|
||||||
|
is_delete: bool,
|
||||||
|
create_at: chrono::NaiveDateTime,
|
||||||
|
update_at: chrono::NaiveDateTime,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(serde::Deserialize, Insertable)]
|
||||||
|
#[diesel(table_name = schema::tags)]
|
||||||
|
pub struct TagForm {
|
||||||
|
pub uid: i64,
|
||||||
|
pub name: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Queryable, Selectable, serde::Serialize)]
|
||||||
|
#[diesel(table_name = schema::users)]
|
||||||
|
pub struct User {
|
||||||
|
pub id: i64,
|
||||||
|
pub username: String,
|
||||||
|
pub password: String,
|
||||||
|
pub mail: String,
|
||||||
|
pub is_delete: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Insertable)]
|
||||||
|
#[diesel(table_name = schema::users)]
|
||||||
|
pub struct UserForm {
|
||||||
|
pub username: String,
|
||||||
|
pub password: String,
|
||||||
|
pub mail: String,
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,2 +1,3 @@
|
|||||||
pub mod db_model;
|
pub mod db_model;
|
||||||
pub mod schema;
|
pub mod schema;
|
||||||
|
pub mod req;
|
||||||
|
|||||||
0
src/model/req.rs
Normal file
0
src/model/req.rs
Normal file
118
src/user/dal.rs
Normal file
118
src/user/dal.rs
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
use diesel::prelude::*;
|
||||||
|
use crate::model::{db_model, schema};
|
||||||
|
use std::error::Error;
|
||||||
|
use std::fmt::Debug;
|
||||||
|
use pbkdf2::password_hash::{PasswordHash, PasswordVerifier};
|
||||||
|
use pbkdf2::Pbkdf2;
|
||||||
|
use serde_json::json;
|
||||||
|
|
||||||
|
pub async fn add_user(app_state: crate::AppState, username: String, password: String, mail: String) -> Result<(), ()> {
|
||||||
|
let conn = app_state
|
||||||
|
.db
|
||||||
|
.get()
|
||||||
|
.await
|
||||||
|
.map_err(|_| {
|
||||||
|
println!("fail to get db connection");
|
||||||
|
()
|
||||||
|
})?;
|
||||||
|
let target_username = username.clone();
|
||||||
|
// 1. check if current username exists.
|
||||||
|
let res = conn.interact(
|
||||||
|
move |conn| {
|
||||||
|
schema::users::table
|
||||||
|
.filter(schema::users::username.eq(target_username.clone()))
|
||||||
|
.count()
|
||||||
|
.get_result::<i64>(conn)
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.map_err(|res| {
|
||||||
|
()
|
||||||
|
})?
|
||||||
|
.map_err(|res| {
|
||||||
|
()
|
||||||
|
})?;
|
||||||
|
println!("ret {}", res);
|
||||||
|
if res > 0 {
|
||||||
|
println!("user already exists.");
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
let new_user_form = db_model::UserForm {
|
||||||
|
username: username.clone(),
|
||||||
|
password: password.clone(),
|
||||||
|
mail: mail.clone(),
|
||||||
|
};
|
||||||
|
// 2. adding user
|
||||||
|
let add_res = conn.interact(
|
||||||
|
move |conn| {
|
||||||
|
diesel::insert_into(schema::users::table)
|
||||||
|
.values(&new_user_form)
|
||||||
|
.returning(db_model::User::as_returning())
|
||||||
|
.get_result(conn)
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.map_err(|e| {
|
||||||
|
()
|
||||||
|
})?
|
||||||
|
.map_err(|e| {
|
||||||
|
()
|
||||||
|
})?;
|
||||||
|
let out = json!(add_res);
|
||||||
|
println!("new user {}", out.to_string());
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn check_user_psw(app_state: crate::AppState, username: String, password: String) -> bool {
|
||||||
|
let conn_res = app_state
|
||||||
|
.db
|
||||||
|
.get()
|
||||||
|
.await
|
||||||
|
.map_err(|_| {
|
||||||
|
println!("fail to get db connection");
|
||||||
|
()
|
||||||
|
});
|
||||||
|
let conn = match conn_res {
|
||||||
|
Ok(res) => res,
|
||||||
|
Err(err) => { return false; }
|
||||||
|
};
|
||||||
|
// 1. get psw hash
|
||||||
|
let query_username = username.clone();
|
||||||
|
let user_rr = conn.interact(
|
||||||
|
|conn| {
|
||||||
|
schema::users::table
|
||||||
|
.filter(schema::users::username.eq(query_username))
|
||||||
|
.select(db_model::User::as_select())
|
||||||
|
.get_results(conn)
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
|
||||||
|
let user_res = match user_rr {
|
||||||
|
Ok(res) => res,
|
||||||
|
Err(_) => return false,
|
||||||
|
};
|
||||||
|
println!("get user_res success");
|
||||||
|
let user = match user_res {
|
||||||
|
Ok(u) => u,
|
||||||
|
Err(_) => return false,
|
||||||
|
};
|
||||||
|
|
||||||
|
println!("get user success");
|
||||||
|
|
||||||
|
if user.len() != 1 {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
println!("get uniq user success");
|
||||||
|
let cur_user = user.get(0);
|
||||||
|
let psw = match cur_user {
|
||||||
|
Some(usr) => usr.password.clone(),
|
||||||
|
None => "".to_string(),
|
||||||
|
};
|
||||||
|
println!("comparing psw, get {}, stored {}.", password.clone(), psw.clone());
|
||||||
|
|
||||||
|
let hash_res = PasswordHash::new(psw.as_str());
|
||||||
|
let hash = match hash_res {
|
||||||
|
Ok(rs) => rs,
|
||||||
|
Err(_) => return false,
|
||||||
|
};
|
||||||
|
let check_res = Pbkdf2.verify_password(password.as_bytes(), &hash);
|
||||||
|
return check_res.is_ok();
|
||||||
|
}
|
||||||
29
src/user/handler.rs
Normal file
29
src/user/handler.rs
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
use axum::{
|
||||||
|
extract::State, http::StatusCode, routing::post, Json, Router
|
||||||
|
};
|
||||||
|
use axum_macros::debug_handler;
|
||||||
|
use crate::middleware::auth::Claims;
|
||||||
|
use super::dal::check_user_psw;
|
||||||
|
|
||||||
|
pub fn get_nest_handlers() -> Router<crate::AppState> {
|
||||||
|
Router::new()
|
||||||
|
.route("/login", post(login))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(serde::Deserialize)]
|
||||||
|
pub struct LoginCredentialRequest {
|
||||||
|
pub username: String,
|
||||||
|
pub password: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[debug_handler]
|
||||||
|
pub async fn login(
|
||||||
|
State(app_state): State<crate::AppState>,
|
||||||
|
Json(payload): Json<LoginCredentialRequest>,
|
||||||
|
) -> Result<(), (StatusCode, String)> {
|
||||||
|
let res = check_user_psw(app_state, payload.username.clone(), payload.password.clone()).await;
|
||||||
|
if !res {
|
||||||
|
return Err((StatusCode::UNAUTHORIZED, "invalid credentials".to_string()));
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
@@ -1 +1,2 @@
|
|||||||
|
pub mod dal;
|
||||||
pub mod handler;
|
pub mod handler;
|
||||||
@@ -1 +1,2 @@
|
|||||||
pub mod req;
|
pub mod req;
|
||||||
|
pub mod pass;
|
||||||
|
|||||||
16
src/util/pass.rs
Normal file
16
src/util/pass.rs
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
use std::error::Error;
|
||||||
|
use pbkdf2::{
|
||||||
|
password_hash::{
|
||||||
|
rand_core::OsRng,
|
||||||
|
PasswordHash,SaltString,
|
||||||
|
},
|
||||||
|
Pbkdf2,
|
||||||
|
};
|
||||||
|
use pbkdf2::password_hash::PasswordHasher;
|
||||||
|
|
||||||
|
pub fn get_pbkdf2_from_psw(password:String) -> Result<String, pbkdf2::password_hash::Error> {
|
||||||
|
let salt = SaltString::generate(&mut OsRng);
|
||||||
|
let password_hash = Pbkdf2.hash_password(password.as_bytes(), &salt)?.to_string();
|
||||||
|
println!("{}",password_hash);
|
||||||
|
return Ok(password_hash)
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user