Skip to content

Commit

Permalink
sqlx
Browse files Browse the repository at this point in the history
  • Loading branch information
chrislearn committed Jan 19, 2025
1 parent f33e157 commit 2e55a91
Show file tree
Hide file tree
Showing 21 changed files with 457 additions and 191 deletions.
18 changes: 0 additions & 18 deletions locales/code_comment.yml
Original file line number Diff line number Diff line change
Expand Up @@ -256,24 +256,6 @@ username:
th: ชื่อผู้ใช้
el: Όνομα χρήστη
da: Brugernavn
generate_a_string_of_a_specified_length:
en: Generate a string of a specified length
zh_CN: 生成指定长度的字符串
zh_TW: 生成指定長度的字串
fr: Générer une chaîne de caractères d'une longueur spécifiée
ja: 指定した長さの文字列を生成する
es: Generar una cadena de una longitud especificada
de: Erzeugen Sie eine Zeichenkette einer bestimmten Länge
ru: Генерировать строку заданной длины
it: Generare una stringa di una lunghezza specificata
pt: Gerar uma string de um comprimento especificado
ko: 지정된 길이의 문자열 생성
no: Generer en streng av en spesifisert lengde
is: Búa til streng af tiltekinni lengd
uk: Генерувати рядок заданої довжини
th: สร้างสตริงความยาวที่ระบุ
el: Δημιουργήστε μια συμβολοσειρά ενός καθορισμένου μήκους
da: Generer en streng af en bestemt længde
contact_support:
en: Contact Support
zh_CN: 联系支持
Expand Down
1 change: 0 additions & 1 deletion src/templates/classic/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ fn create_files(project_path: &Path, user_selected: Selected, new_cmd: &NewCmd)
"config_error_read":t!("config_error_read"),
"config_error_parse":t!("config_error_parse"),
"config_error_read_failed":t!("config_error_read_failed"),
"generate_a_string_of_a_specified_length":t!("generate_a_string_of_a_specified_length"),
"username":t!("username"),
"password":t!("password"),
"incorrect_password":t!("incorrect_password"),
Expand Down
8 changes: 3 additions & 5 deletions templates/classic/_base/src/hoops/jwt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,7 @@ use crate::config::{self, JwtConfig};

#[derive(Debug, Serialize, Deserialize)]
pub struct JwtClaims {
username: String,
user_id: String,
uid: String,
exp: i64,
}

Expand All @@ -26,11 +25,10 @@ pub fn auth_hoop(config: &JwtConfig) -> JwtAuth<JwtClaims, ConstDecoder> {
.force_passed(false)
}

pub fn get_token(username: String, user_id: String) -> Result<(String, i64)> {
pub fn get_token(uid: impl Into<String>) -> Result<(String, i64)> {
let exp = OffsetDateTime::now_utc() + Duration::seconds(config::get().jwt.expiry);
let claim = JwtClaims {
username,
user_id,
uid: uid.into(),
exp: exp.unix_timestamp(),
};
let token: String = jsonwebtoken::encode(
Expand Down
8 changes: 5 additions & 3 deletions templates/classic/_base/src/main.rs.liquid
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@


{%- if db_lib == "diesel" %}
use std::sync::Arc;
use std::time::Duration;
{%- endif %}

{%- if db_lib == "diesel" %}
use diesel::r2d2;
Expand Down Expand Up @@ -69,8 +73,6 @@ async fn main() {

{%- if db_lib == "diesel" %}
let thread_pool = Arc::new(ScheduledThreadPool::new(config.db.helper_threads));


let db_primary = {
let db_connection_config = db::ConnectionConfig {
statement_timeout: config.db.statement_timeout,
Expand All @@ -81,7 +83,7 @@ async fn main() {
.min_idle(config.db.min_idle)
.connection_timeout(Duration::from_millis(config.db.connection_timeout))
.connection_customizer(Box::new(db_connection_config))
.thread_pool(thread_pool.clone());
.thread_pool(thread_pool);

db::DieselPool::new(&config.db.url, &config.db, db_config).expect("diesel pool should be created")
};
Expand Down
2 changes: 1 addition & 1 deletion templates/classic/_base/src/routers/demo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use salvo::prelude::*;
use crate::AppResult;

#[handler]
pub async fn hello(req: &mut Request) -> AppResult<Text<String>>{
pub async fn hello(req: &mut Request) -> AppResult<Text<String>> {
#[derive(Template)]
#[template(path = "hello.html")]
struct HelloTemplate<'a> {
Expand Down
6 changes: 4 additions & 2 deletions templates/classic/_base/src/routers/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use rust_embed::RustEmbed;
use salvo::prelude::*;
use salvo::serve_static::{EmbeddedFileExt, static_embed};
use salvo::serve_static::{static_embed, EmbeddedFileExt};

mod auth;
mod demo;
Expand All @@ -13,7 +13,9 @@ use crate::{config, hoops};
struct Assets;

pub fn root() -> Router {
let favicon = Assets::get("favicon.ico").expect("favicon not found").into_handler();
let favicon = Assets::get("favicon.ico")
.expect("favicon not found")
.into_handler();
let router = Router::new()
.hoop(Logger::new())
.get(demo::hello)
Expand Down
5 changes: 2 additions & 3 deletions templates/classic/_base/src/utils/mod.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
use anyhow::Context;
use argon2::{password_hash::SaltString, Argon2, PasswordHash};
use rand::Rng;
use std::iter;
/// {{generate_a_string_of_a_specified_length}}

#[allow(dead_code)]
#[inline]
pub fn random_string(limit: usize) -> String {
Expand All @@ -13,7 +12,7 @@ pub fn random_string(limit: usize) -> String {
.collect()
}

pub async fn verify_password(password: &str, password_hash: String) -> anyhow::Result<()> {
pub async fn verify_password(password: &str, password_hash: &str) -> anyhow::Result<()> {
let hash = PasswordHash::new(&password_hash)
.map_err(|e| anyhow::anyhow!("invalid password hash: {}", e))?;
let result = hash.verify_password(&[&Argon2::default()], password);
Expand Down
2 changes: 1 addition & 1 deletion templates/classic/diesel/.env.liquid
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
DATABASE_URL=postgresql://postgres:root@localhost/{{project_name}}
{%- endif %}
{%- if db_type == "sqlite" %}
DATABASE_URL="sqlite:data/{{project_name}}.db"
DATABASE_URL="sqlite:data/{{project_name}}.sqlite"
{%- endif %}
{%- if db_type == "mysql" %}
DATABASE_URL="mysql://root:root@localhost/{{project_name}}"
Expand Down
8 changes: 4 additions & 4 deletions templates/classic/diesel/src/routers/auth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,18 +44,18 @@ pub async fn post_login(
) -> JsonResult<LoginOutData> {
let idata = idata.into_inner();
let conn = &mut db::connect()?;
let Some((id, username, hashed)) = users::table
let Some(User{id, username, password}) = users::table
.filter(users::username.eq(&idata.username))
.select((users::id, users::username, users::password))
.first::<(String, String, String)>(conn)
.first::<User>(conn)
.optional()?
else {
return Err(StatusError::unauthorized()
.brief("User does not exist.")
.into());
};

if utils::verify_password(&idata.password, hashed)
if utils::verify_password(&idata.password, &password)
.await
.is_err()
{
Expand All @@ -64,7 +64,7 @@ pub async fn post_login(
.into());
}

let (token, exp) = jwt::get_token(username.clone(), id.clone())?;
let (token, exp) = jwt::get_token(&username, &id)?;
let odata = LoginOutData {
id,
username,
Expand Down
File renamed without changes.
80 changes: 80 additions & 0 deletions templates/classic/mongodb/src/routers/auth.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
use cookie::Cookie;
use diesel::prelude::*;
use rinja::Template;
use salvo::oapi::extract::*;
use salvo::prelude::*;
use serde::{Deserialize, Serialize};

use crate::hoops::jwt;
use crate::schema::*;
use crate::{db, json_ok, utils, AppResult, JsonResult};

#[handler]
pub async fn login_page(res: &mut Response) -> AppResult<()> {
#[derive(Template)]
#[template(path = "login.html")]
struct LoginTemplate {}
if let Some(cookie) = res.cookies().get("jwt_token") {
let token = cookie.value().to_string();
if jwt::decode_token(&token) {
res.render(Redirect::other("/users"));
return Ok(());
}
}
let hello_tmpl = LoginTemplate {};
res.render(Text::Html(hello_tmpl.render().unwrap()));
Ok(())
}
#[derive(Deserialize, ToSchema, Default, Debug)]
pub struct LoginInData {
pub username: String,
pub password: String,
}
#[derive(Serialize, ToSchema, Default, Debug)]
pub struct LoginOutData {
pub id: String,
pub username: String,
pub token: String,
pub exp: i64,
}
#[endpoint(tags("auth"))]
pub async fn post_login(
idata: JsonBody<LoginInData>,
res: &mut Response,
) -> JsonResult<LoginOutData> {
let idata = idata.into_inner();
let conn = &mut db::connect()?;
let Some((id, username, hashed)) = users::table
.filter(users::username.eq(&idata.username))
.select((users::id, users::username, users::password))
.first::<(String, String, String)>(conn)
.optional()?
else {
return Err(StatusError::unauthorized()
.brief("User does not exist.")
.into());
};

if utils::verify_password(&idata.password, hashed)
.await
.is_err()
{
return Err(StatusError::unauthorized()
.brief("Addount not exist or password is incorrect.")
.into());
}

let (token, exp) = jwt::get_token(username.clone(), id.clone())?;
let odata = LoginOutData {
id,
username,
token,
exp,
};
let cookie = Cookie::build(("jwt_token", odata.token.clone()))
.path("/")
.http_only(true)
.build();
res.add_cookie(cookie);
json_ok(odata)
}
99 changes: 99 additions & 0 deletions templates/classic/mongodb/src/routers/user.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
use diesel::prelude::*;
use rinja::Template;
use salvo::oapi::extract::*;
use salvo::prelude::*;
use serde::Deserialize;
use ulid::Ulid;
use validator::Validate;

use crate::models::{SafeUser, User};
use crate::schema::*;
use crate::{db, empty_ok, json_ok, utils, AppResult, EmptyResult, JsonResult};

#[derive(Template)]
#[template(path = "user_list_page.html")]
pub struct UserListPageTemplate {}

#[derive(Template)]
#[template(path = "user_list_frag.html")]
pub struct UserListFragTemplate {}

#[handler]
pub async fn 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 = UserListFragTemplate {};
res.render(Text::Html(hello_tmpl.render().unwrap()));
}
None => {
let hello_tmpl = UserListPageTemplate {};
res.render(Text::Html(hello_tmpl.render().unwrap()));
}
}
Ok(())
}

#[derive(Deserialize, Debug, Validate, ToSchema, Default)]
pub struct CreateInData {
#[validate(length(min = 5, message = "username length must be greater than 5"))]
pub username: String,
#[validate(length(min = 6, message = "password length must be greater than 5"))]
pub password: String,
}
#[endpoint(tags("users"))]
pub async fn create_user(idata: JsonBody<CreateInData>) -> JsonResult<SafeUser> {
let CreateInData { username, password } = idata.into_inner();
let conn = &mut db::connect()?;
let user = User {
id: Ulid::new().to_string(),
username,
password: utils::hash_password(&password).await?,
};
diesel::insert_into(users::table)
.values(&user)
.execute(conn)?;
let User { id, username, .. } = user;
json_ok(SafeUser { id, username })
}

#[derive(Deserialize, Debug, Validate, ToSchema)]
struct UpdateInData {
#[validate(length(min = 5, message = "username length must be greater than 5"))]
username: String,
#[validate(length(min = 6, message = "password length must be greater than 5"))]
password: String,
}
#[endpoint(tags("users"), parameters(("id", description = "user id")))]
pub async fn update_user(
user_id: PathParam<String>,
idata: JsonBody<UpdateInData>,
) -> JsonResult<SafeUser> {
let user_id = user_id.into_inner();
let UpdateInData { username, password } = idata.into_inner();
let conn = &mut db::connect()?;
diesel::update(users::table.find(&user_id))
.set((
users::username.eq(&username),
users::password.eq(utils::hash_password(&password).await?),
))
.execute(conn)?;
json_ok(SafeUser {
id: user_id,
username,
})
}

#[endpoint(tags("users"))]
pub async fn delete_user(user_id: PathParam<String>) -> EmptyResult {
let conn = &mut db::connect()?;
diesel::delete(users::table.find(user_id.into_inner())).execute(conn)?;
empty_ok()
}

#[endpoint(tags("users"))]
pub async fn list_users() -> JsonResult<Vec<SafeUser>> {
let conn = &mut db::connect()?;
let users = users::table.select(SafeUser::as_select()).load(conn)?;
json_ok(users)
}
10 changes: 10 additions & 0 deletions templates/classic/sqlx/.env.liquid
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@

{%- if db_type == "postgres" %}
DATABASE_URL=postgresql://postgres:root@localhost/{{project_name}}
{%- endif %}
{%- if db_type == "sqlite" %}
DATABASE_URL="sqlite:data/{{project_name}}.sqlite"
{%- endif %}
{%- if db_type == "mysql" %}
DATABASE_URL="mysql://root:root@localhost/{{project_name}}"
{%- endif %}
Loading

0 comments on commit 2e55a91

Please sign in to comment.