From b0ab9b0363c00538158495a3352984c50b67c19e Mon Sep 17 00:00:00 2001 From: Fankai Liu Date: Mon, 16 Oct 2023 22:11:53 +0800 Subject: [PATCH 1/6] add decode_token fn --- src/template/src/middleware/jwt.rs | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/template/src/middleware/jwt.rs b/src/template/src/middleware/jwt.rs index 164e650..b7088c4 100644 --- a/src/template/src/middleware/jwt.rs +++ b/src/template/src/middleware/jwt.rs @@ -1,5 +1,5 @@ use anyhow::Result; -use jsonwebtoken::EncodingKey; +use jsonwebtoken::{decode, Algorithm, DecodingKey, EncodingKey, Validation}; use salvo::jwt_auth::{ConstDecoder, CookieFinder, HeaderFinder, QueryFinder}; use salvo::prelude::*; use serde::{Deserialize, Serialize}; @@ -13,7 +13,6 @@ pub struct JwtClaims { exp: i64, } -#[allow(dead_code)] pub fn jwt_hoop() -> JwtAuth { let auth_handler: JwtAuth = JwtAuth::new(ConstDecoder::from_secret( CFG.jwt.jwt_secret.to_owned().as_bytes(), @@ -35,10 +34,21 @@ pub fn get_token(username: String, user_id: String) -> Result<(String, i64)> { user_id, exp: exp.unix_timestamp(), }; - let token = jsonwebtoken::encode( + let token: String = jsonwebtoken::encode( &jsonwebtoken::Header::default(), &claim, &EncodingKey::from_secret(CFG.jwt.jwt_secret.as_bytes()), )?; Ok((token, exp.unix_timestamp())) } + +#[allow(dead_code)] +pub fn decode_token(token: &str) -> bool { + let validation = Validation::new(Algorithm::HS256); + decode::( + token, + &DecodingKey::from_secret(CFG.jwt.jwt_secret.as_bytes()), + &validation, + ) + .is_ok() +} From cd2b36284f7982115742da7689f799873ef3eef3 Mon Sep 17 00:00:00 2001 From: Fankai Liu Date: Tue, 17 Oct 2023 21:15:48 +0800 Subject: [PATCH 2/6] add user button --- src/template/templates/user_list.hbs | 214 ++++++++++++++++----------- 1 file changed, 130 insertions(+), 84 deletions(-) diff --git a/src/template/templates/user_list.hbs b/src/template/templates/user_list.hbs index f17a058..4f48ae3 100644 --- a/src/template/templates/user_list.hbs +++ b/src/template/templates/user_list.hbs @@ -1,96 +1,142 @@
-
-
-
-

- {{user_list}} -

-
+
+
+
+

Users

+

+ A list of all the users in your account including their name, title, + email and role. +

-
-
-
- - - - - - - - - - {% for user in users %} - - - - + {% for user in users %} @@ -77,39 +72,10 @@ - - - - + From eab580793ff7a94b81aa9cfa830485990506c252 Mon Sep 17 00:00:00 2001 From: Fankai Liu Date: Fri, 20 Oct 2023 22:55:39 +0800 Subject: [PATCH 5/6] update template --- src/template/src/dtos/user.hbs | 10 +- src/template/src/routers/mod.hbs | 50 +++---- src/template/src/routers/user.hbs | 141 +++++++++++--------- src/template/templates/login.hbs | 2 +- src/template/templates/user_list.hbs | 152 ++++++++++++++-------- src/template/templates/user_list_page.hbs | 33 +---- 6 files changed, 210 insertions(+), 178 deletions(-) diff --git a/src/template/src/dtos/user.hbs b/src/template/src/dtos/user.hbs index c45fea8..ddbcfd2 100644 --- a/src/template/src/dtos/user.hbs +++ b/src/template/src/dtos/user.hbs @@ -1,4 +1,4 @@ -use salvo::prelude::ToSchema; +use salvo::prelude::{ToSchema, Extractible}; use serde::{Deserialize, Serialize}; #[derive(Deserialize, Debug, ToSchema, Default)] @@ -13,17 +13,15 @@ pub struct UserLoginRequest { pub password: String, } -#[derive(Debug, Deserialize, ToSchema, Default)] +#[derive(Debug, Deserialize,Extractible,ToSchema, Default)] +#[salvo(extract(default_source(from = "body", format = "json")))] pub struct UserUpdateRequest { + #[salvo(extract(source(from = "param")))] pub id: String, pub username: String, pub password: String, } -#[derive(Debug, Deserialize, ToSchema, Default)] -pub struct UserDeleteRequest { - pub id: String, -} #[derive(Debug, Serialize, ToSchema, Default)] pub struct UserResponse { diff --git a/src/template/src/routers/mod.hbs b/src/template/src/routers/mod.hbs index 04ae14c..39f1739 100644 --- a/src/template/src/routers/mod.hbs +++ b/src/template/src/routers/mod.hbs @@ -5,7 +5,6 @@ use salvo::{ prelude::{CatchPanic, Logger, OpenApi, SwaggerUi}, Router, }; - use self::{ demo::hello, {{#if need_db_conn}} @@ -27,35 +26,36 @@ pub mod user; {{/if}} pub fn router() -> Router { + {{#if is_web_site}} + {{#if need_db_conn}} + let mut no_auth_routers = vec![ + Router::with_path("login").get(login_page), + Router::with_path("/api/login").post(post_login), + ]; + + let mut need_auth_routers = vec![ + Router::with_path("users") + .post(post_add_user) + .get(user_list_page), + Router::with_path("/api/users") + .get(get_users) + .put(post_update_user) + .push(Router::with_path("").delete(delete_user)), + ]; let router = Router::new() .hoop(Logger::new()) .hoop(CatchPanic::new()) - {{#if need_db_conn}} .get(hello) - {{#if is_web_site}} - .push(Router::with_path("login").get(login_page).post(post_login)) - {{else}} - .push(Router::with_path("login").post(post_login)) - {{/if}} - .push(user_router().hoop(jwt_hoop())); - {{else}} - .get(hello); - {{/if}} + .append(&mut no_auth_routers) + .push( + Router::new() + .append(&mut need_auth_routers) + .hoop(jwt_hoop()), + ); + {{/if}} + {{/if}} let doc = OpenApi::new("salvo web api", "0.0.1").merge_router(&router); router .push(doc.into_router("/api-doc/openapi.json")) .push(SwaggerUi::new("/api-doc/openapi.json").into_router("swagger-ui")) -} -{{#if need_db_conn}} -pub fn user_router() -> Router { - Router::with_path("user") - .post(post_add_user) - .put(post_update_user) - .delete(delete_user) - {{#if is_web_site}} - .get(user_list_page) - {{else}} - .get(get_users) - {{/if}} -} -{{/if}} \ No newline at end of file +} \ No newline at end of file diff --git a/src/template/src/routers/user.hbs b/src/template/src/routers/user.hbs index 8ad1089..d4d4ef1 100644 --- a/src/template/src/routers/user.hbs +++ b/src/template/src/routers/user.hbs @@ -1,48 +1,24 @@ {{#if is_web_site}} -use askama::Template; -{{/if}} -{{#if is_web_site}} -use salvo::{ - endpoint, - http::cookie::Cookie, - oapi::extract::{FormBody, JsonBody}, - writing::{Redirect, Text}, - Response, -}; -use crate::{ - app_error::AppResult, - app_response::{ErrRes, Res}, - dtos::user::{ - UserAddRequest, UserDeleteRequest, UserLoginRequest, UserResponse, UserUpdateRequest, - }, - services::user, -}; -{{else}} -use salvo::{ - endpoint, - oapi::extract::{FormBody, JsonBody}, - Response, -}; -use crate::{ - app_response::{ErrRes, Res}, - dtos::user::{ - UserAddRequest, UserDeleteRequest, UserLoginRequest, UserUpdateRequest, - }, - services::user, -}; -{{/if}} -{{#if is_web_site}} #[derive(Template)] #[template(path = "login.html")] struct LoginTemplate {} #[endpoint] pub async fn login_page(res: &mut Response) -> AppResult<()> { + let cookies = res.cookies(); + let cookie = cookies.get("jwt_token"); + if let Some(cookie) = cookie { + let token = cookie.value().to_string(); + if decode_token(&token) { + res.render(Redirect::other("/users")); + return Ok(()); + } else { + } + } let hello_tmpl = LoginTemplate {}; res.render(Text::Html(hello_tmpl.render().unwrap())); Ok(()) } -{{/if}} #[endpoint] pub async fn post_add_user(req: JsonBody, res: &mut Response) { @@ -54,8 +30,9 @@ pub async fn post_add_user(req: JsonBody, res: &mut Response) { } #[endpoint] -pub async fn post_update_user(req: JsonBody, res: &mut Response) { - let result = user::update_user(req.0).await; +pub async fn put_update_user(req: &mut Request, res: &mut Response) { + let req: UserUpdateRequest = req.extract().await.unwrap(); + let result = user::update_user(req).await; match result { Ok(data) => Res::with_data(data).into_response(res), Err(e) => ErrRes::with_err(&e.to_string()).into_response(res), @@ -63,8 +40,8 @@ pub async fn post_update_user(req: JsonBody, res: &mut Respon } #[endpoint] -pub async fn delete_user(req: JsonBody, res: &mut Response) { - let result = user::delete_user(req.0).await; +pub async fn delete_user(id: PathParam, res: &mut Response) { + let result = user::delete_user(id.0).await; match result { Ok(_) => Res::with_data(()).into_response(res), Err(e) => ErrRes::with_err(&e.to_string()).into_response(res), @@ -79,26 +56,11 @@ pub async fn get_users(res: &mut Response) { Err(e) => ErrRes::with_err(&e.to_string()).into_response(res), } } -{{#if is_web_site}} - -#[derive(Template)] -#[template(path = "user_list_page.html")] -pub struct UserListPageTemplate { - users: Vec, -} - -#[endpoint] -pub async fn user_list_page(res: &mut Response) -> AppResult<()> { - let users = user::users().await?; - let hello_tmpl = UserListPageTemplate { users }; - - res.render(Text::Html(hello_tmpl.render().unwrap())); - Ok(()) -} #[endpoint] -pub async fn post_login(req: FormBody, res: &mut Response) { - let result = user::login(req.0).await; +pub async fn post_login(req: JsonBody, res: &mut Response) { + let result: Result = + user::login(req.0).await; match result { Ok(data) => { let jwt_token = data.token.clone(); @@ -107,15 +69,76 @@ pub async fn post_login(req: FormBody, res: &mut Response) { .http_only(true) .finish(); res.add_cookie(cookie); - res.render(Redirect::other("/user")); } Err(e) => ErrRes::with_err(&e.to_string()).into_response(res), } } +#[derive(Template)] +#[template(path = "user_list_page.html")] +pub struct UserListPageTemplate {} + +#[derive(Template)] +#[template(path = "user_list.html")] +pub struct UserListTemplate {} + +#[endpoint] +pub async fn user_list_page(req: &mut Request, res: &mut Response) -> AppResult<()> { + let is_fragment = req.headers().get("X-Fragment-Header"); + match is_fragment { + Some(_) => { + let hello_tmpl = UserListTemplate {}; + res.render(Text::Html(hello_tmpl.render().unwrap())); + } + None => { + let hello_tmpl = UserListPageTemplate {}; + res.render(Text::Html(hello_tmpl.render().unwrap())); + } + } + Ok(()) +} {{else}} +use salvo::{ + endpoint, + oapi::extract::{FormBody, JsonBody}, + Response, +}; +use crate::{ + app_response::{ErrRes, Res}, + dtos::user::{ + UserAddRequest, UserDeleteRequest, UserLoginRequest, UserUpdateRequest, + }, + services::user, +}; #[endpoint] -pub async fn post_login(req: FormBody, res: &mut Response) { - let result = user::login(req.0).await; +pub async fn post_add_user(req: JsonBody, res: &mut Response) { + let result = user::add_user(req.0).await; + match result { + Ok(data) => Res::with_data(data).into_response(res), + Err(e) => ErrRes::with_err(&e.to_string()).into_response(res), + } +} + +#[endpoint] +pub async fn post_update_user(req: JsonBody, res: &mut Response) { + let result = user::update_user(req.0).await; + match result { + Ok(data) => Res::with_data(data).into_response(res), + Err(e) => ErrRes::with_err(&e.to_string()).into_response(res), + } +} + +#[endpoint] +pub async fn delete_user(req: JsonBody, res: &mut Response) { + let result = user::delete_user(req.0).await; + match result { + Ok(_) => Res::with_data(()).into_response(res), + Err(e) => ErrRes::with_err(&e.to_string()).into_response(res), + } +} + +#[endpoint] +pub async fn get_users(res: &mut Response) { + let result = user::users().await; match result { Ok(data) => Res::with_data(data).into_response(res), Err(e) => ErrRes::with_err(&e.to_string()).into_response(res), diff --git a/src/template/templates/login.hbs b/src/template/templates/login.hbs index ac66f49..30e67cb 100644 --- a/src/template/templates/login.hbs +++ b/src/template/templates/login.hbs @@ -113,7 +113,7 @@ }, }); this.userList = await userListResponse.text(); - history.pushState(null, '', '/user'); + //history.pushState(null, '', '/user'); } catch (error) { Swal.fire({ title: "Error!", diff --git a/src/template/templates/user_list.hbs b/src/template/templates/user_list.hbs index dd02a58..69d9a20 100644 --- a/src/template/templates/user_list.hbs +++ b/src/template/templates/user_list.hbs @@ -1,4 +1,4 @@ -
+
@@ -7,8 +7,7 @@
- {{username}} - - {{delete}} - {{operation}} -
+ + + +
+
+
+ + + + + + + + + + {% for user in users %} + + + + - - - {% endfor %} - - -
+ Name + + Delete + Delete +
+ {{user.username}} + + 删除删除 - [[user.username]] - { - return fetch('/user', { - method: 'DELETE', + return fetch("/user", { + method: "DELETE", headers: { - 'Content-Type': 'application/json' + "Content-Type": "application/json", }, body: JSON.stringify({ - id: '[[user.id]]', - }) - }) - .then(response => { - if (!response.ok) { - throw new Error(response.statusText) - } - location.reload(); - return + id: userId, + }), }) - .catch(error => { - Swal.showValidationMessage( - `Request failed: ${error}` - ) + .then((response) => { + if (!response.ok) { + throw new Error(response.statusText); + } + location.reload(); + return; + }) + .catch((error) => { + Swal.showValidationMessage(`Request failed: ${error}`); + }); + }, + allowOutsideClick: () => !Swal.isLoading(), + }); + }; + const addUser = () => { + Swal.fire({ + title: "添加用户", + html: ` + + + `, + preConfirm: () => { + return fetch("/user", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + username: document.getElementById("swal-input1").value, + password: document.getElementById("swal-input2").value, + }), }) + .then((response) => { + if (!response.ok) { + throw new Error(response.statusText); + } + location.reload(); + return; + }) + .catch((error) => { + Swal.showValidationMessage(`Request failed: ${error}`); + }); }, - allowOutsideClick: () => !Swal.isLoading() - }) - " - >{{delete}}{{delete}} -
-
-
-
- - + allowOutsideClick: () => !Swal.isLoading(), + }); + }; + From 6081a1b6d427efd4bfb9facd49f061d16b26b8ac Mon Sep 17 00:00:00 2001 From: Fankai Liu Date: Wed, 18 Oct 2023 22:11:19 +0800 Subject: [PATCH 3/6] Optimize deletion behavior --- src/template/templates/user_list.hbs | 46 ++++------------------------ 1 file changed, 6 insertions(+), 40 deletions(-) diff --git a/src/template/templates/user_list.hbs b/src/template/templates/user_list.hbs index 4f48ae3..dd02a58 100644 --- a/src/template/templates/user_list.hbs +++ b/src/template/templates/user_list.hbs @@ -3,10 +3,7 @@

Users

-

- A list of all the users in your account including their name, title, - email and role. -

+

A list of all the users

+ hx-delete="/user/{{user.id}}" > 删除删除deletedelete
- - - {% for user in users %} - - - -
- Delete Delete
- {{user.username}} - - deletedelete +
@@ -71,38 +60,87 @@
+ diff --git a/src/template/templates/user_list_page.hbs b/src/template/templates/user_list_page.hbs index d7f65dc..7958716 100644 --- a/src/template/templates/user_list_page.hbs +++ b/src/template/templates/user_list_page.hbs @@ -7,34 +7,7 @@ salvo {% include "user_list.html" %} - - - - - - - + + + From ea7977b124a9900f16e44b8cd0e3bd6c4170b6c0 Mon Sep 17 00:00:00 2001 From: Fankai Liu Date: Sat, 21 Oct 2023 14:10:48 +0800 Subject: [PATCH 6/6] better web site --- src/template/src/main_template.hbs | 10 ++ src/template/src/middleware/jwt.rs | 1 + src/template/src/routers/mod.hbs | 114 ++++++++++++++++++---- src/template/src/routers/user.hbs | 113 ++++++++++++++------- src/template/src/services/user.hbs | 14 +-- src/template/templates/login.hbs | 87 ++++++++++++++++- src/template/templates/user_list.hbs | 84 ---------------- src/template/templates/user_list_page.hbs | 91 ++++++++++++++++- src/utils/create_project.rs | 2 +- src/utils/get_selection.rs | 6 +- 10 files changed, 362 insertions(+), 160 deletions(-) diff --git a/src/template/src/main_template.hbs b/src/template/src/main_template.hbs index a262acf..fa997b0 100644 --- a/src/template/src/main_template.hbs +++ b/src/template/src/main_template.hbs @@ -45,6 +45,11 @@ async fn main() { "swagger-ui: https://{}/swagger-ui", &CFG.server.address.replace("0.0.0.0", "127.0.0.1") ); + {{#if is_web_site}} + {{#if need_db_conn}} + println!("login page: https://{}/login", &CFG.server.address.replace("0.0.0.0", "127.0.0.1")); + {{/if}} + {{/if}} let config = RustlsConfig::new( Keycert::new() .cert(CERT_KEY.cert.clone()) @@ -68,6 +73,11 @@ async fn main() { "swagger-ui: http://{}/swagger-ui", &CFG.server.address.replace("0.0.0.0", "127.0.0.1") ); + {{#if is_web_site}} + {{#if need_db_conn}} + println!("login page: http://{}/login", &CFG.server.address.replace("0.0.0.0", "127.0.0.1")); + {{/if}} + {{/if}} let acceptor = TcpListener::new(&CFG.server.address).bind().await; let server = Server::new(acceptor).serve_with_graceful_shutdown( service, diff --git a/src/template/src/middleware/jwt.rs b/src/template/src/middleware/jwt.rs index b7088c4..cac61b0 100644 --- a/src/template/src/middleware/jwt.rs +++ b/src/template/src/middleware/jwt.rs @@ -13,6 +13,7 @@ pub struct JwtClaims { exp: i64, } +#[allow(dead_code)] pub fn jwt_hoop() -> JwtAuth { let auth_handler: JwtAuth = JwtAuth::new(ConstDecoder::from_secret( CFG.jwt.jwt_secret.to_owned().as_bytes(), diff --git a/src/template/src/routers/mod.hbs b/src/template/src/routers/mod.hbs index 39f1739..39b1a1c 100644 --- a/src/template/src/routers/mod.hbs +++ b/src/template/src/routers/mod.hbs @@ -1,33 +1,22 @@ {{#if need_db_conn}} + {{#if is_web_site}} use crate::middleware::jwt::jwt_hoop; -{{/if}} use salvo::{ prelude::{CatchPanic, Logger, OpenApi, SwaggerUi}, Router, }; + use self::{ demo::hello, -{{#if need_db_conn}} -{{#if is_web_site}} user::{ - delete_user,login_page, post_add_user, post_login, post_update_user, + delete_user, get_users, login_page, post_add_user, post_login, put_update_user, user_list_page, }, -{{else}} - user::{ - delete_user, get_users, post_add_user, post_login, post_update_user, - }, -{{/if}} -{{/if}} }; pub mod demo; -{{#if need_db_conn}} pub mod user; -{{/if}} pub fn router() -> Router { - {{#if is_web_site}} - {{#if need_db_conn}} let mut no_auth_routers = vec![ Router::with_path("login").get(login_page), Router::with_path("/api/login").post(post_login), @@ -35,12 +24,14 @@ pub fn router() -> Router { let mut need_auth_routers = vec![ Router::with_path("users") + .get(user_list_page), + Router::with_path("/api/users").get(get_users) .post(post_add_user) - .get(user_list_page), - Router::with_path("/api/users") - .get(get_users) - .put(post_update_user) - .push(Router::with_path("").delete(delete_user)), + .push( + Router::with_path("") + .put(put_update_user) + .delete(delete_user), + ), ]; let router = Router::new() .hoop(Logger::new()) @@ -52,10 +43,91 @@ pub fn router() -> Router { .append(&mut need_auth_routers) .hoop(jwt_hoop()), ); + let doc = OpenApi::new("salvo web api", "0.0.1").merge_router(&router); + router + .push(doc.into_router("/api-doc/openapi.json")) + .push(SwaggerUi::new("/api-doc/openapi.json").into_router("swagger-ui")) +} + {{else}} +use crate::middleware::jwt::jwt_hoop; +use salvo::{ + prelude::{CatchPanic, Logger, OpenApi, SwaggerUi}, + Router, +}; + +use self::{ + demo::hello, + user::{ + delete_user, get_users, post_add_user, post_login, put_update_user, + }, +}; +pub mod demo; +pub mod user; + +pub fn router() -> Router { + let mut no_auth_routers = vec![ + Router::with_path("/api/login").post(post_login), + ]; + + let mut need_auth_routers = vec![ + Router::with_path("/api/users").get(get_users) + .post(post_add_user) + .push( + Router::with_path("") + .put(put_update_user) + .delete(delete_user), + ), + ]; + let router = Router::new() + .hoop(Logger::new()) + .hoop(CatchPanic::new()) + .get(hello) + .append(&mut no_auth_routers) + .push( + Router::new() + .append(&mut need_auth_routers) + .hoop(jwt_hoop()), + ); + let doc = OpenApi::new("salvo web api", "0.0.1").merge_router(&router); + router + .push(doc.into_router("/api-doc/openapi.json")) + .push(SwaggerUi::new("/api-doc/openapi.json").into_router("swagger-ui")) +} {{/if}} - {{/if}} +{{else}} + {{#if is_web_site}} +use salvo::{ + prelude::{CatchPanic, Logger, OpenApi, SwaggerUi}, + Router, +}; +use self::demo::hello; +pub mod demo; +pub fn router() -> Router { + let router = Router::new() + .hoop(Logger::new()) + .hoop(CatchPanic::new()) + .get(hello); let doc = OpenApi::new("salvo web api", "0.0.1").merge_router(&router); router .push(doc.into_router("/api-doc/openapi.json")) .push(SwaggerUi::new("/api-doc/openapi.json").into_router("swagger-ui")) -} \ No newline at end of file +} + {{else}} +use salvo::{ + prelude::{CatchPanic, Logger, OpenApi, SwaggerUi}, + Router, +}; +use self::demo::hello; +pub mod demo; +pub fn router() -> Router { + let router = Router::new() + .hoop(Logger::new()) + .hoop(CatchPanic::new()) + .get(hello); + let doc = OpenApi::new("salvo web api", "0.0.1").merge_router(&router); + router + .push(doc.into_router("/api-doc/openapi.json")) + .push(SwaggerUi::new("/api-doc/openapi.json").into_router("swagger-ui")) +} + {{/if}} +{{/if}} \ No newline at end of file diff --git a/src/template/src/routers/user.hbs b/src/template/src/routers/user.hbs index d4d4ef1..4144290 100644 --- a/src/template/src/routers/user.hbs +++ b/src/template/src/routers/user.hbs @@ -1,9 +1,25 @@ {{#if is_web_site}} +use crate::{ + app_error::AppResult, + app_response::{ErrRes, Res}, + dtos::user::{UserAddRequest, UserLoginRequest, UserLoginResponse, UserUpdateRequest}, + middleware::jwt::decode_token, + services::user, +}; +use askama::Template; +use salvo::{ + endpoint, + http::cookie::Cookie, + oapi::extract::{JsonBody, PathParam}, + writing::{Redirect, Text}, + Request, Response, +}; + #[derive(Template)] #[template(path = "login.html")] struct LoginTemplate {} -#[endpoint] +#[endpoint( tags("comm"),)] pub async fn login_page(res: &mut Response) -> AppResult<()> { let cookies = res.cookies(); let cookie = cookies.get("jwt_token"); @@ -19,8 +35,23 @@ pub async fn login_page(res: &mut Response) -> AppResult<()> { res.render(Text::Html(hello_tmpl.render().unwrap())); Ok(()) } +#[endpoint( tags("comm"),)] +pub async fn post_login(req: JsonBody, res: &mut Response) { + let result: AppResult = user::login(req.0).await; + match result { + Ok(data) => { + let jwt_token = data.token.clone(); + let cookie = Cookie::build("jwt_token", jwt_token) + .path("/") + .http_only(true) + .finish(); + res.add_cookie(cookie); + } + Err(e) => ErrRes::with_err(&e.to_string()).into_response(res), + } +} -#[endpoint] +#[endpoint( tags("users"))] pub async fn post_add_user(req: JsonBody, res: &mut Response) { let result = user::add_user(req.0).await; match result { @@ -29,7 +60,10 @@ pub async fn post_add_user(req: JsonBody, res: &mut Response) { } } -#[endpoint] +#[endpoint( tags("users"), +parameters( + ("id", description = "user id"), +))] pub async fn put_update_user(req: &mut Request, res: &mut Response) { let req: UserUpdateRequest = req.extract().await.unwrap(); let result = user::update_user(req).await; @@ -39,7 +73,7 @@ pub async fn put_update_user(req: &mut Request, res: &mut Response) { } } -#[endpoint] +#[endpoint( tags("users"),)] pub async fn delete_user(id: PathParam, res: &mut Response) { let result = user::delete_user(id.0).await; match result { @@ -48,7 +82,7 @@ pub async fn delete_user(id: PathParam, res: &mut Response) { } } -#[endpoint] +#[endpoint( tags("users"),)] pub async fn get_users(res: &mut Response) { let result = user::users().await; match result { @@ -57,22 +91,6 @@ pub async fn get_users(res: &mut Response) { } } -#[endpoint] -pub async fn post_login(req: JsonBody, res: &mut Response) { - let result: Result = - user::login(req.0).await; - match result { - Ok(data) => { - let jwt_token = data.token.clone(); - let cookie = Cookie::build("jwt_token", jwt_token) - .path("/") - .http_only(true) - .finish(); - res.add_cookie(cookie); - } - Err(e) => ErrRes::with_err(&e.to_string()).into_response(res), - } -} #[derive(Template)] #[template(path = "user_list_page.html")] pub struct UserListPageTemplate {} @@ -97,19 +115,36 @@ pub async fn user_list_page(req: &mut Request, res: &mut Response) -> AppResult< Ok(()) } {{else}} -use salvo::{ - endpoint, - oapi::extract::{FormBody, JsonBody}, - Response, -}; use crate::{ + app_error::AppResult, app_response::{ErrRes, Res}, - dtos::user::{ - UserAddRequest, UserDeleteRequest, UserLoginRequest, UserUpdateRequest, - }, + dtos::user::{UserAddRequest, UserLoginRequest, UserLoginResponse, UserUpdateRequest}, services::user, }; -#[endpoint] +use salvo::{ + endpoint, + http::cookie::Cookie, + oapi::extract::{JsonBody, PathParam}, + Request, Response, +}; + +#[endpoint( tags("comm"),)] +pub async fn post_login(req: JsonBody, res: &mut Response) { + let result: AppResult = user::login(req.0).await; + match result { + Ok(data) => { + let jwt_token = data.token.clone(); + let cookie = Cookie::build("jwt_token", jwt_token) + .path("/") + .http_only(true) + .finish(); + res.add_cookie(cookie); + } + Err(e) => ErrRes::with_err(&e.to_string()).into_response(res), + } +} + +#[endpoint( tags("users"))] pub async fn post_add_user(req: JsonBody, res: &mut Response) { let result = user::add_user(req.0).await; match result { @@ -118,25 +153,29 @@ pub async fn post_add_user(req: JsonBody, res: &mut Response) { } } -#[endpoint] -pub async fn post_update_user(req: JsonBody, res: &mut Response) { - let result = user::update_user(req.0).await; +#[endpoint( tags("users"), +parameters( + ("id", description = "user id"), +))] +pub async fn put_update_user(req: &mut Request, res: &mut Response) { + let req: UserUpdateRequest = req.extract().await.unwrap(); + let result = user::update_user(req).await; match result { Ok(data) => Res::with_data(data).into_response(res), Err(e) => ErrRes::with_err(&e.to_string()).into_response(res), } } -#[endpoint] -pub async fn delete_user(req: JsonBody, res: &mut Response) { - let result = user::delete_user(req.0).await; +#[endpoint( tags("users"),)] +pub async fn delete_user(id: PathParam, res: &mut Response) { + let result = user::delete_user(id.0).await; match result { Ok(_) => Res::with_data(()).into_response(res), Err(e) => ErrRes::with_err(&e.to_string()).into_response(res), } } -#[endpoint] +#[endpoint( tags("users"),)] pub async fn get_users(res: &mut Response) { let result = user::users().await; match result { diff --git a/src/template/src/services/user.hbs b/src/template/src/services/user.hbs index 9ef7eff..c8f9e22 100644 --- a/src/template/src/services/user.hbs +++ b/src/template/src/services/user.hbs @@ -3,7 +3,7 @@ use crate::{ app_error::AppResult, db::DB, dtos::user::{ - UserAddRequest, UserDeleteRequest, UserLoginRequest, UserLoginResponse, UserResponse, + UserAddRequest, UserLoginRequest, UserLoginResponse, UserResponse, UserUpdateRequest, }, middleware::jwt::get_token, @@ -16,7 +16,7 @@ use crate::{ app_error::AppResult, db::DB, dtos::user::{ - UserAddRequest, UserDeleteRequest, UserLoginRequest, UserLoginResponse, UserResponse, + UserAddRequest, UserLoginRequest, UserLoginResponse, UserResponse, UserUpdateRequest, }, middleware::jwt::get_token, @@ -103,14 +103,14 @@ pub async fn update_user(req: UserUpdateRequest) -> AppResult { }) } -pub async fn delete_user(req: UserDeleteRequest) -> AppResult<()> { +pub async fn delete_user(id: String) -> AppResult<()> { let db = DB.get().ok_or(anyhow::anyhow!("{{database_connection_failed}}"))?; sqlx::query!( r#" DELETE FROM users WHERE id = $1 "#, - req.id, + id, ) .execute(db) .await?; @@ -196,9 +196,9 @@ pub async fn update_user(req: UserUpdateRequest) -> AppResult { }) } -pub async fn delete_user(req: UserDeleteRequest) -> AppResult<()> { - let db = DB.get().ok_or(anyhow::anyhow!("{{database_connection_failed}}"))?; - User::delete_by_id(req.id, ).exec(db).await?; +pub async fn delete_user(id: String) -> AppResult<()> { + let db = DB.get().ok_or(anyhow::anyhow!("数据库连接失败"))?; + User::delete_by_id(id).exec(db).await?; Ok(()) } diff --git a/src/template/templates/login.hbs b/src/template/templates/login.hbs index 30e67cb..cfe249d 100644 --- a/src/template/templates/login.hbs +++ b/src/template/templates/login.hbs @@ -92,7 +92,7 @@ userList: "", async submit() { try { - const response = await fetch("/login", { + const response = await fetch("/api/login", { method: "POST", headers: { "Content-Type": "application/json", @@ -107,13 +107,13 @@ throw new Error(`${data.msg}`); } this.isLoggedIn = true; - const userListResponse = await fetch("/user", { + const userListResponse = await fetch("/users", { headers: { "X-Fragment-Header": "true", }, }); this.userList = await userListResponse.text(); - //history.pushState(null, '', '/user'); + history.pushState(null, '', '/users'); } catch (error) { Swal.fire({ title: "Error!", @@ -125,5 +125,86 @@ }, }; } + function userForm() { + return { + users: [], + fetchData() { + fetch("/api/users") + .then((response) => { + if (!response.ok) { + throw new Error("Network response was not ok"); + } + return response.json(); + }) + .then((data) => { + this.users = data.data; + }) + .catch((error) => { + console.error( + "There has been a problem with your fetch operation:", + error + ); + }); + }, + addUser() { + Swal.fire({ + title: "Add User", + showCancelButton: true, + html: ` + + + `, + preConfirm: () => { + return fetch("/api/users", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + username: document.getElementById("swal-input1").value, + password: document.getElementById("swal-input2").value, + }), + }) + .then((response) => { + if (!response.ok) { + throw new Error(response.statusText); + } + this.fetchData(); + return; + }) + .catch((error) => { + Swal.showValidationMessage(`Request failed: ${error}`); + }); + }, + allowOutsideClick: () => !Swal.isLoading(), + }); + }, + deleteUser(id) { + Swal.fire({ + title: "Are you sure?", + text: "You won't be able to revert this!", + icon: "warning", + showCancelButton: true, + confirmButtonText: "Yes, delete it!", + preConfirm: () => { + return fetch(`/api/users/${id}`, { + method: "DELETE", + }) + .then((response) => { + if (!response.ok) { + throw new Error(response.statusText); + } + this.fetchData(); + return; + }) + .catch((error) => { + Swal.showValidationMessage(`Request failed: ${error}`); + }); + }, + allowOutsideClick: () => !Swal.isLoading(), + }).then((result) => {}); + }, + }; + } diff --git a/src/template/templates/user_list.hbs b/src/template/templates/user_list.hbs index 69d9a20..55ce689 100644 --- a/src/template/templates/user_list.hbs +++ b/src/template/templates/user_list.hbs @@ -60,87 +60,3 @@
- - diff --git a/src/template/templates/user_list_page.hbs b/src/template/templates/user_list_page.hbs index 7958716..df98780 100644 --- a/src/template/templates/user_list_page.hbs +++ b/src/template/templates/user_list_page.hbs @@ -6,8 +6,91 @@ salvo - {% include "user_list.html" %} - - - + {% include "user_list.html" %} + + + + diff --git a/src/utils/create_project.rs b/src/utils/create_project.rs index 6695414..ef2ee71 100644 --- a/src/utils/create_project.rs +++ b/src/utils/create_project.rs @@ -83,7 +83,7 @@ fn write_project_file( "jsonwebtoken": "8.3.0", "once_cell": "1.18.0", "salvo": { - "version": "*", + "version": "0.57", "features": ["anyhow", "logging", "cors", "oapi", "jwt-auth", "rustls", "catch-panic","cookie"] }, "serde": "1.0.188", diff --git a/src/utils/get_selection.rs b/src/utils/get_selection.rs index 49796d5..1a305a6 100644 --- a/src/utils/get_selection.rs +++ b/src/utils/get_selection.rs @@ -18,8 +18,8 @@ pub fn get_user_selected() -> Result> { ..ColorfulTheme::default() }; let selections = &[ - t!("salvo_web_api"), t!("salvo_web_site"), + t!("salvo_web_api"), // "custom", ]; let selection = Select::with_theme(&theme) @@ -28,8 +28,8 @@ pub fn get_user_selected() -> Result> { .items(&selections[..]) .interact()?; let template_type = match selection { - 0 => TemplateType::SalvoWebApi, - 1 => TemplateType::SalvoWebSite, + 0 => TemplateType::SalvoWebSite, + 1 => TemplateType::SalvoWebApi, _ => anyhow::bail!("Invalid selection"), }; let db_conn_types = &[