From 0fc4da0baba39796bff80de10b3b988757cb8b7e Mon Sep 17 00:00:00 2001 From: Fankai Liu Date: Sun, 1 Oct 2023 17:13:23 +0800 Subject: [PATCH 01/19] add db options --- locales/app.yml | 126 ++++++++++++++++++++++++++++++++++++ src/utils/create_project.rs | 47 ++------------ src/utils/get_selection.rs | 100 ++++++++++++++++++++++++++++ src/utils/mod.rs | 1 + 4 files changed, 233 insertions(+), 41 deletions(-) create mode 100644 src/utils/get_selection.rs diff --git a/locales/app.yml b/locales/app.yml index 8605862..ff37518 100644 --- a/locales/app.yml +++ b/locales/app.yml @@ -257,3 +257,129 @@ error_project_path_exist: th: ปลายทาง `%{path}` มีอยู่แล้ว el: ο προορισμός `%{path}` υπάρχει ήδη da: destination `%{path}` findes allerede +select_db_conn_type: + en: select database connection type + zh_CN: 选择数据库连接类型 + zh_TW: 選擇資料庫連接類型 + fr: sélectionnez le type de connexion à la base de données + ja: データベース接続タイプを選択 + es: seleccione el tipo de conexión a la base de datos + de: wählen Sie den Datenbankverbindungstyp aus + ru: выберите тип подключения к базе данных + it: seleziona il tipo di connessione al database + pt: selecione o tipo de conexão ao banco de dados + ko: 데이터베이스 연결 유형을 선택하십시오 + no: velg databaseforbindelsestype + is: veldu gagnagrunns tengingar tegund + uk: виберіть тип підключення до бази даних + th: เลือกประเภทการเชื่อมต่อฐานข้อมูล + el: επιλέξτε τύπο σύνδεσης βάσης δεδομένων + da: vælg databaseforbindelsestype +db_conn_types_sqlx: + en: SQLx is an async, pure Rust† SQL crate featuring compile-time checked queries without a DSL. 10K⭐ + zh_CN: SQLx 是一个异步、纯 Rust† 的 SQL 库,具有无 DSL 的编译时检查查询。10K⭐ + zh_TW: SQLx 是一個非同步、純 Rust† 的 SQL 函式庫,具有無 DSL 的編譯時檢查查詢。10K⭐ + fr: SQLx est une caisse SQL asynchrone et pure Rust† avec des requêtes vérifiées au moment de la compilation sans DSL. 10K⭐ + ja: SQLx は、DSL なしでコンパイル時にクエリをチェックする、非同期で純粋な Rust† SQL クレートです。10K⭐ + es: SQLx es una caja SQL asíncrona y pura Rust† con consultas verificadas en tiempo de compilación sin DSL. 10K⭐ + de: SQLx ist eine asynchrone, reine Rust† SQL-Kiste mit kompilierten Abfragen ohne DSL. 10K⭐ + ru: SQLx - это асинхронный, чистый Rust† SQL ящик с проверкой запросов во время компиляции без DSL. 10K⭐ + it: SQLx è una cassa SQL asincrona e pura Rust† con query verificate in fase di compilazione senza DSL. 10K⭐ + pt: SQLx é uma caixa SQL assíncrona e pura Rust† com consultas verificadas no momento da compilação sem DSL. 10K⭐ + ko: SQLx는 DSL 없이 컴파일 시간에 쿼리를 확인하는 비동기, 순수 Rust† SQL 크레이트입니다. 10K⭐ + no: SQLx er en asynkron, ren Rust† SQL-kasse med kompilerte spørringer uten DSL. 10K⭐ + is: SQLx er asynkron, hrein Rust† SQL kassa með samþjöppuð fyrirspurnir án DSL. 10K⭐ + uk: SQLx - це асинхронний, чистий Rust† SQL ящик з перевіреними на етапі компіляції запитами без DSL. 10K⭐ + th: SQLx เป็น SQL crate แบบ async, pure Rust† ที่มีการตรวจสอบคิวรีในเวลาคอมไพล์โดยไม่มี DSL. 10K⭐ + el: το SQLx είναι ένα ασύγχρονο, καθαρό Rust† SQL κιβώτιο με ερωτήματα ελεγμένα κατά τη σύνταξη χωρίς DSL. 10K⭐ + da: SQLx er en asynkron, ren Rust† SQL-kasse med kompilerede forespørgsler uden DSL. 10K⭐ +db_conn_types_diesel: + en: diesel:A safe, extensible ORM and Query Builder for Rust 11K⭐ + zh_CN: diesel:Rust 的安全、可扩展的 ORM 和查询生成器 11K⭐ + zh_TW: diesel:Rust 的安全、可擴展的 ORM 和查詢生成器 11K⭐ + fr: diesel:ORM et générateur de requêtes extensible et sûr pour Rust 11K⭐ + ja: diesel:Rust の安全で拡張可能な ORM とクエリビルダー 11K⭐ + es: diesel:ORM y generador de consultas seguro y extensible para Rust 11K⭐ + de: diesel:Eine sichere, erweiterbare ORM und Abfragegenerator für Rust 11K⭐ + ru: diesel:ORM и генератор запросов безопасны и расширяемы для Rust 11K⭐ + it: diesel:ORM e generatore di query sicuro ed estensibile per Rust 11K⭐ + pt: diesel:ORM e gerador de consultas seguro e extensível para Rust 11K⭐ + ko: diesel:Rust의 안전하고 확장 가능한 ORM 및 쿼리 빌더 11K⭐ + no: diesel:En sikker, utvidbar ORM og spørringsbygger for Rust 11K⭐ + is: diesel:Öruggt, útvíkkandi ORM og fyrirspurnarbyggir fyrir Rust 11K⭐ + uk: diesel:ORM та генератор запитів безпечні та розширювані для Rust 11K⭐ + th: diesel:ORM และ Query Builder ที่ปลอดภัยและสามารถขยายได้สำหรับ Rust 11K⭐ + el: diesel:ένα ασφαλές, επεκτάσιμο ORM και Query Builder για Rust 11K⭐ + da: diesel:En sikker, udvidelig ORM og Query Builder til Rust 11K⭐ +db_conn_types_sea_orm: + en: sea-orm:🐚 An async & dynamic ORM for Rust (I like it) + zh_CN: sea-orm:🐚 Rust 的异步和动态 ORM(我喜欢它) + zh_TW: sea-orm:🐚 Rust 的非同步和動態 ORM(我喜歡它) + fr: sea-orm:🐚 Un ORM asynchrone et dynamique pour Rust (je l'aime) + ja: sea-orm:🐚 Rust の非同期 & 動的 ORM(気に入っています) + es: sea-orm:🐚 Un ORM asíncrono y dinámico para Rust (me gusta) + de: sea-orm:🐚 Ein asynchrones und dynamisches ORM für Rust (ich mag es) + ru: sea-orm:🐚 Асинхронный и динамический ORM для Rust (мне нравится) + it: sea-orm:🐚 Un ORM asincrono e dinamico per Rust (mi piace) + pt: sea-orm:🐚 Um ORM assíncrono e dinâmico para Rust (eu gosto) + ko: sea-orm:🐚 Rust의 비동기 및 동적 ORM (나는 좋아) + no: sea-orm:🐚 En asynkron og dynamisk ORM for Rust (jeg liker det) + is: sea-orm:🐚 Asynkron og hreyfanleg ORM fyrir Rust (mér líkar það) + uk: sea-orm:🐚 Асинхронний та динамічний ORM для Rust (мені подобається) + th: sea-orm:🐚 ORM แบบ async และ dynamic สำหรับ Rust (ฉันชอบมัน) + el: sea-orm:🐚 ένα ασύγχρονο και δυναμικό ORM για Rust (μου αρέσει) + da: sea-orm:🐚 En asynkron og dynamisk ORM til Rust (jeg kan lide det) +db_conn_types_rbatis: + en: rbatis:A similar Mybatis (java) asynchronous ORM framework + zh_CN: rbatis:类似 Mybatis (java) 的异步 ORM 框架 + zh_TW: rbatis:類似 Mybatis (java) 的非同步 ORM 框架 + fr: rbatis:Un cadre ORM asynchrone similaire à Mybatis (java) + ja: rbatis:Mybatis (java) に似た非同期 ORM フレームワーク + es: rbatis:Un marco ORM asíncrono similar a Mybatis (java) + de: rbatis:Ein ähnliches Mybatis (java) asynchrones ORM-Framework + ru: rbatis:Асинхронный ORM-фреймворк, похожий на Mybatis (java) + it: rbatis:Un framework ORM asincrono simile a Mybatis (java) + pt: rbatis:Um framework ORM assíncrono semelhante ao Mybatis (java) + ko: rbatis:Mybatis (java)와 유사한 비동기 ORM 프레임 워크 + no: rbatis:Et lignende Mybatis (java) asynkront ORM-rammeverk + is: rbatis:Asynkron ORM kerfi sem líkist Mybatis (java) + uk: rbatis:Асинхронний ORM-фреймворк, схожий на Mybatis (java) + th: rbatis:เฟรมเวิร์ก ORM แบบ async ที่คล้ายกับ Mybatis (java) + el: rbatis:ένα παρόμοιο Mybatis (java) ασύγχρονο πλαίσιο ORM + da: rbatis:Et lignende Mybatis (java) asynkront ORM-rammeverk +db_conn_types_nothing: + en: unnecessary + zh_CN: 不需要 + zh_TW: 不需要 + fr: inutile + ja: 不要 + es: innecesario + de: unnötig + ru: ненужный + it: inutile + pt: desnecessário + ko: 불필요한 + no: unødvendig + is: óþarfi + uk: непотрібний + th: ไม่จำเป็น + el: περιττός + da: unødvendig +select_db_type: + en: select database type + zh_CN: 选择数据库类型 + zh_TW: 選擇資料庫類型 + fr: sélectionnez le type de base de données + ja: データベースタイプを選択 + es: seleccione el tipo de base de datos + de: wählen Sie den Datenbanktyp aus + ru: выберите тип базы данных + it: seleziona il tipo di database + pt: selecione o tipo de banco de dados + ko: 데이터베이스 유형을 선택하십시오 + no: velg databasetype + is: veldu gagnagrunnstegund + uk: виберіть тип бази даних + th: เลือกประเภทฐานข้อมูล + el: επιλέξτε τύπο βάσης δεδομένων + da: vælg databasetype \ No newline at end of file diff --git a/src/utils/create_project.rs b/src/utils/create_project.rs index a22a3e8..ea968ae 100644 --- a/src/utils/create_project.rs +++ b/src/utils/create_project.rs @@ -1,6 +1,5 @@ use crate::Project; use anyhow::{Context, Result}; -use dialoguer::{console::Style, theme::ColorfulTheme, Select}; use handlebars::Handlebars; use print_util::success; use rust_i18n::t; @@ -14,7 +13,7 @@ use std::{ slice, }; -use super::{print_util, restricted_names, warning}; +use super::{print_util, restricted_names, warning, get_selection::{get_user_selected, UserSelected, TemplateType}}; pub fn create_project(project: Project) -> Result<()> { check_name(&project.project_name)?; @@ -25,14 +24,14 @@ pub fn create_project(project: Project) -> Result<()> { } check_path(project_path)?; - let config = init_config()?; + let config = get_user_selected()?; match config { Some(config) => { - write_project_file(project_path, config, project.clone())?; + // write_project_file(project_path, config, project.clone())?; - init_git(project_path)?; + // init_git(project_path)?; - success(t!("create_success", project_name = project_name).replace(r"\n", "\n")); + // success(t!("create_success", project_name = project_name).replace(r"\n", "\n")); } None => anyhow::bail!("cli quit!"), } @@ -40,7 +39,7 @@ pub fn create_project(project: Project) -> Result<()> { Ok(()) } -fn write_project_file(project_path: &Path, config: Config, project: Project) -> Result<()> { +fn write_project_file(project_path: &Path, config: UserSelected, project: Project) -> Result<()> { let handlebars = Handlebars::new(); let is_web_site = config.template_type == TemplateType::SalvoWebSite; let data = json!({ @@ -210,37 +209,3 @@ fn _create_dir_all(p: &Path) -> Result<()> { Ok(()) } -#[derive(Debug)] -pub struct Config { - pub template_type: TemplateType, -} - -fn init_config() -> Result> { - let theme = ColorfulTheme { - defaults_style: Style::new().blue(), - prompt_style: Style::new().green().bold(), - values_style: Style::new().yellow().dim(), - ..ColorfulTheme::default() - }; - let selections = &[ - t!("salvo_web_api"), - t!("salvo_web_site"), - // "custom", - ]; - let selection = Select::with_theme(&theme) - .with_prompt(t!("welcome_message").replace(r"\n", "\n")) - .default(0) - .items(&selections[..]) - .interact()?; - let template_type = match selection { - 0 => TemplateType::SalvoWebApi, - 1 => TemplateType::SalvoWebSite, - _ => anyhow::bail!("Invalid selection"), - }; - Ok(Some(Config { template_type })) -} -#[derive(Debug, PartialEq)] -pub enum TemplateType { - SalvoWebSite, - SalvoWebApi, -} diff --git a/src/utils/get_selection.rs b/src/utils/get_selection.rs new file mode 100644 index 0000000..e29a7cc --- /dev/null +++ b/src/utils/get_selection.rs @@ -0,0 +1,100 @@ +use anyhow::Result; +use rust_i18n::t; +use dialoguer::{theme::ColorfulTheme, Select, console::Style}; + +#[derive(Debug)] +pub struct UserSelected { + pub template_type: TemplateType, + pub db_type: DbType, + pub db_conn_type: DbConnectionType, +} + + +pub fn get_user_selected() -> Result> { + let theme = ColorfulTheme { + defaults_style: Style::new().blue(), + prompt_style: Style::new().green().bold(), + active_item_style: Style::new().blue().bold(), + values_style: Style::new().blue().dim(), + ..ColorfulTheme::default() + }; + let selections = &[ + t!("salvo_web_api"), + t!("salvo_web_site"), + // "custom", + ]; + let selection = Select::with_theme(&theme) + .with_prompt(t!("welcome_message").replace(r"\n", "\n")) + .default(0) + .items(&selections[..]) + .interact()?; + let template_type = match selection { + 0 => TemplateType::SalvoWebApi, + 1 => TemplateType::SalvoWebSite, + _ => anyhow::bail!("Invalid selection"), + }; + let db_conn_types = &[ + t!("db_conn_types_sqlx"), + t!("db_conn_types_diesel"), + t!("db_conn_types_sea_orm"), + t!("db_conn_types_rbatis"), + t!("db_conn_types_nothing"), + // "custom", + ]; + let db_conn_type_selection = Select::with_theme(&theme) + .with_prompt(t!("select_db_conn_type").replace(r"\n", "\n")) + .default(0) + .items(&db_conn_types[..]) + .interact()?; + + let db_conn_type = match db_conn_type_selection { + 0 => DbConnectionType::Sqlx, + 1 => DbConnectionType::Diesel, + 2 => DbConnectionType::SeaOrm, + 3 => DbConnectionType::Rbatis, + 4 => DbConnectionType::Nothing, + _ => anyhow::bail!("Invalid db connection type selection"), + }; + + + let db_types = &[ + "sqlite", + "mysql", + "postgres", + // "custom", + ]; + let db_type_selection = Select::with_theme(&theme) + .with_prompt(t!("select_db_type").replace(r"\n", "\n")) + .default(0) + .items(&db_types[..]) + .interact()?; + let db_type = match db_type_selection { + 0 => DbType::Sqlite, + 1 => DbType::Mysql, + 2 => DbType::Postgres, + _ => anyhow::bail!("Invalid db type selection"), + }; + + Ok(Some(UserSelected { template_type, db_type, db_conn_type })) +} +#[derive(Debug, PartialEq)] +pub enum TemplateType { + SalvoWebSite, + SalvoWebApi, +} + +#[derive(Debug, PartialEq)] +pub enum DbType { + Sqlite, + Mysql, + Postgres, +} + +#[derive(Debug,PartialEq)] +pub enum DbConnectionType { + Sqlx, + Diesel, + SeaOrm, + Rbatis, + Nothing, +} \ No newline at end of file diff --git a/src/utils/mod.rs b/src/utils/mod.rs index ea1fae6..27ccd30 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -1,5 +1,6 @@ pub mod create_project; mod print_util; mod restricted_names; +pub mod get_selection; pub use create_project::create_project; pub use print_util::{error, print_logo, success, warning}; From 58f7f583ce9c98ded225d129756848b112b63f4c Mon Sep 17 00:00:00 2001 From: Fankai Liu Date: Mon, 2 Oct 2023 22:31:38 +0800 Subject: [PATCH 02/19] add urils template --- src/template/utils/mod.rs | 1 + src/template/utils/rand_utils.rs | 38 ++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+) create mode 100644 src/template/utils/mod.rs create mode 100644 src/template/utils/rand_utils.rs diff --git a/src/template/utils/mod.rs b/src/template/utils/mod.rs new file mode 100644 index 0000000..c6c1b4b --- /dev/null +++ b/src/template/utils/mod.rs @@ -0,0 +1 @@ +pub mod rand_utils; diff --git a/src/template/utils/rand_utils.rs b/src/template/utils/rand_utils.rs new file mode 100644 index 0000000..ed05e3e --- /dev/null +++ b/src/template/utils/rand_utils.rs @@ -0,0 +1,38 @@ +use anyhow::Context; +use argon2::{password_hash::SaltString, Argon2, PasswordHash}; +use rand::Rng; +use std::iter; +/// 生成指定长度的字符串 +#[inline] +pub fn random_string(limit: usize) -> String { + iter::repeat(()) + .map(|_| rand::thread_rng().sample(rand::distributions::Alphanumeric)) + .map(char::from) + .take(limit) + .collect() +} + +pub async fn verify_password(password: String, password_hash: String) -> anyhow::Result<()> { + tokio::task::spawn_blocking(move || -> 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); + match result { + Ok(_) => Ok(()), + Err(_) => Err(anyhow::anyhow!("invalid password")), + } + }) + .await + .context("panic in verifying password hash")? +} + +pub async fn hash_password(password: String) -> anyhow::Result { + tokio::task::spawn_blocking(move || -> anyhow::Result { + let salt = SaltString::generate(rand::thread_rng()); + Ok(PasswordHash::generate(Argon2::default(), password, &salt) + .map_err(|e| anyhow::anyhow!("failed to generate password hash: {}", e))? + .to_string()) + }) + .await + .context("panic in generating password hash")? +} From 5a7e9c9d3c80381a507a0e0b0b9c6e42a236f330 Mon Sep 17 00:00:00 2001 From: Fankai Liu Date: Tue, 3 Oct 2023 22:22:50 +0800 Subject: [PATCH 03/19] add dots --- src/template/dtos/mod.rs | 1 + src/template/dtos/user.rs | 40 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+) create mode 100644 src/template/dtos/mod.rs create mode 100644 src/template/dtos/user.rs diff --git a/src/template/dtos/mod.rs b/src/template/dtos/mod.rs new file mode 100644 index 0000000..22d12a3 --- /dev/null +++ b/src/template/dtos/mod.rs @@ -0,0 +1 @@ +pub mod user; diff --git a/src/template/dtos/user.rs b/src/template/dtos/user.rs new file mode 100644 index 0000000..c45fea8 --- /dev/null +++ b/src/template/dtos/user.rs @@ -0,0 +1,40 @@ +use salvo::prelude::ToSchema; +use serde::{Deserialize, Serialize}; + +#[derive(Deserialize, Debug, ToSchema, Default)] +pub struct UserAddRequest { + pub username: String, + pub password: String, +} + +#[derive(Debug, Deserialize, ToSchema, Default)] +pub struct UserLoginRequest { + pub username: String, + pub password: String, +} + +#[derive(Debug, Deserialize, ToSchema, Default)] +pub struct UserUpdateRequest { + 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 { + pub id: String, + pub username: String, +} + +#[derive(Debug, Serialize, ToSchema, Default)] +pub struct UserLoginResponse { + pub id: String, + pub username: String, + pub token: String, + pub exp: i64, +} From 3ee44c662be219cdc39a3f0793c279a5b128b49a Mon Sep 17 00:00:00 2001 From: Fankai Liu Date: Wed, 4 Oct 2023 22:42:17 +0800 Subject: [PATCH 04/19] add sqlx --- .../src/{app_error.rs => app_error.hbs} | 15 ++- src/template/{ => src}/dtos/mod.rs | 0 src/template/{ => src}/dtos/user.rs | 0 src/template/src/main_template.hbs | 89 ++++--------- src/template/src/middleware/handle404.hbs | 26 ++++ src/template/src/middleware/jwt.rs | 12 +- src/template/src/middleware/mod.rs | 1 + src/template/src/models/mod.rs | 1 + src/template/src/models/user.rs | 9 ++ src/template/src/routers/demo.hbs | 48 +++++++ src/template/src/routers/mod.hbs | 43 ++++++ src/template/src/routers/user.hbs | 108 +++++++++++++++ src/template/src/services/mod.rs | 1 + src/template/src/services/user.hbs | 124 ++++++++++++++++++ src/template/templates/login.html | 111 ++++++++++++++++ src/template/templates/user_list.html | 96 ++++++++++++++ src/template/templates/user_list_page.html | 40 ++++++ src/utils/create_project.rs | 22 +++- 18 files changed, 670 insertions(+), 76 deletions(-) rename src/template/src/{app_error.rs => app_error.hbs} (65%) rename src/template/{ => src}/dtos/mod.rs (100%) rename src/template/{ => src}/dtos/user.rs (100%) create mode 100644 src/template/src/middleware/handle404.hbs create mode 100644 src/template/src/models/mod.rs create mode 100644 src/template/src/models/user.rs create mode 100644 src/template/src/routers/demo.hbs create mode 100644 src/template/src/routers/mod.hbs create mode 100644 src/template/src/routers/user.hbs create mode 100644 src/template/src/services/mod.rs create mode 100644 src/template/src/services/user.hbs create mode 100644 src/template/templates/login.html create mode 100644 src/template/templates/user_list.html create mode 100644 src/template/templates/user_list_page.html diff --git a/src/template/src/app_error.rs b/src/template/src/app_error.hbs similarity index 65% rename from src/template/src/app_error.rs rename to src/template/src/app_error.hbs index af030aa..998ae09 100644 --- a/src/template/src/app_error.rs +++ b/src/template/src/app_error.hbs @@ -1,6 +1,9 @@ use salvo::{ - async_trait, http::ParseError, prelude::EndpointOutRegister, writing::Json, Depot, Request, - Response, Writer, + async_trait, + http::ParseError, + prelude::{EndpointOutRegister, StatusCode}, + writing::Json, + Depot, Request, Response, Writer, }; use thiserror::Error; @@ -10,6 +13,10 @@ pub enum AppError { AnyHow(#[from] anyhow::Error), #[error("http::ParseError:`{0}`")] ParseError(#[from] ParseError), + {{#if is_sqlx}} + #[error("sqlx::Error:`{0}`")] + SqlxError(#[from] sqlx::Error), + {{/if}} } pub type AppResult = Result; @@ -17,7 +24,11 @@ pub type AppResult = Result; #[async_trait] impl Writer for AppError { async fn write(mut self, _req: &mut Request, _depot: &mut Depot, res: &mut Response) { + {{#if is_web_site}} + res.stuff(StatusCode::INTERNAL_SERVER_ERROR, Json(self.to_string())); + {{else}} res.render(Json(self.to_string())); + {{/if}} } } diff --git a/src/template/dtos/mod.rs b/src/template/src/dtos/mod.rs similarity index 100% rename from src/template/dtos/mod.rs rename to src/template/src/dtos/mod.rs diff --git a/src/template/dtos/user.rs b/src/template/src/dtos/user.rs similarity index 100% rename from src/template/dtos/user.rs rename to src/template/src/dtos/user.rs diff --git a/src/template/src/main_template.hbs b/src/template/src/main_template.hbs index 20433e7..1216b87 100644 --- a/src/template/src/main_template.hbs +++ b/src/template/src/main_template.hbs @@ -1,71 +1,44 @@ -use crate::app_error::AppResult; +{{#if need_db_coon}} +use crate::db::init_db_conn; +{{/if}} +use crate::middleware::handle404::handle_404; +use crate::routers::router; use config::{CERT_KEY, CFG}; -use middleware::jwt::jwt_hoop; use salvo::catcher::Catcher; use salvo::conn::rustls::{Keycert, RustlsConfig}; use salvo::prelude::*; use tokio::sync::oneshot; - mod app_error; +mod app_response; mod config; +{{#if need_db_coon}} +mod db; +mod dtos; +mod services; +{{/if}} mod middleware; -{{#if is_web_site}} - -#[derive(Template)] -#[template(path = "hello.html")] -struct HelloTemplate<'a> { - name: &'a str, -} - -#[endpoint] -async fn hello(req: &mut Request, res: &mut Response)->AppResult<()>{ - let hello_tmpl = HelloTemplate { - name: req.param::<&str>("name").unwrap_or("World"), - }; - res.render(Text::Html(hello_tmpl.render().unwrap())); - Ok(()) -} - -#[derive(Template)] -#[template(path = "404.html")] -struct Handle404 { -} +mod models; +mod routers; +mod utils; -#[handler] -async fn handle404(&self, _req: &Request, _depot: &Depot, res: &mut Response, ctrl: &mut FlowCtrl) { - if let Some(StatusCode::NOT_FOUND) = res.status_code { - let handle_404 = Handle404{}; - res.render(Text::Html(handle_404.render().unwrap())); - ctrl.skip_rest(); - } -} -{{else}} -#[endpoint] -async fn hello() -> AppResult<&'static str> { - Ok("Hello World from salvo") -} - -#[handler] -async fn handle404(&self, _req: &Request, _depot: &Depot, res: &mut Response, ctrl: &mut FlowCtrl) { - if let Some(StatusCode::NOT_FOUND) = res.status_code { - res.render(Json("404 not found")); - ctrl.skip_rest(); - } -} -{{/if}} #[tokio::main] async fn main() { //{{main_log_message}} init_log(); - + init_db_conn().await; let (tx, rx) = oneshot::channel(); let router = router(); let service: Service = router.into(); - let service = service.catcher(Catcher::default().hoop(handle404)); - println!("💨 {} is staring ",&CFG.server.name); - println!(" listen on {}",&CFG.server.address.replace("0.0.0.0", "127.0.0.1")); + let service = service.catcher(Catcher::default().hoop(handle_404)); + println!("💨 {} is staring ", &CFG.server.name); + println!(" listen on {}", &CFG.server.address); + match CFG.server.ssl { true => { + println!( + "swagger-ui: https://{}/swagger-ui", + &CFG.server.address.replace("0.0.0.0", "127.0.0.1") + ); let config = RustlsConfig::new( Keycert::new() .cert(CERT_KEY.cert.clone()) @@ -85,6 +58,10 @@ async fn main() { tokio::task::spawn(server); } false => { + println!( + "swagger-ui: http://{}/swagger-ui", + &CFG.server.address.replace("0.0.0.0", "127.0.0.1") + ); let acceptor = TcpListener::new(&CFG.server.address).bind().await; let server = Server::new(acceptor).serve_with_graceful_shutdown( service, @@ -102,18 +79,6 @@ async fn main() { let _ = tx.send(()); } -fn router() -> Router { - let router = Router::new() - .hoop(Logger::new()) - .hoop(CatchPanic::new()) - .hoop(jwt_hoop()) - .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")) -} - fn init_log() { let _guard = clia_tracing_config::build() .filter_level(&CFG.log.filter_level) diff --git a/src/template/src/middleware/handle404.hbs b/src/template/src/middleware/handle404.hbs new file mode 100644 index 0000000..4764e5e --- /dev/null +++ b/src/template/src/middleware/handle404.hbs @@ -0,0 +1,26 @@ +use askama::Template; +use salvo::{handler, prelude::StatusCode, writing::Text, Depot, FlowCtrl, Request, Response}; + +{{#if is_web_site}} +#[derive(Template)] +#[template(path = "404.html")] +struct Handle404 { +} + +#[handler] +async fn handle_404(&self, _req: &Request, _depot: &Depot, res: &mut Response, ctrl: &mut FlowCtrl) { + if let Some(StatusCode::NOT_FOUND) = res.status_code { + let handle_404 = Handle404{}; + res.render(Text::Html(handle_404.render().unwrap())); + ctrl.skip_rest(); + } +} +{{else}} +#[handler] +async fn handle_404(&self, _req: &Request, _depot: &Depot, res: &mut Response, ctrl: &mut FlowCtrl) { + if let Some(StatusCode::NOT_FOUND) = res.status_code { + res.render(Json("404 not found")); + ctrl.skip_rest(); + } +} +{{/if}} \ No newline at end of file diff --git a/src/template/src/middleware/jwt.rs b/src/template/src/middleware/jwt.rs index 0fd553e..b335522 100644 --- a/src/template/src/middleware/jwt.rs +++ b/src/template/src/middleware/jwt.rs @@ -1,6 +1,6 @@ use anyhow::Result; use jsonwebtoken::EncodingKey; -use salvo::jwt_auth::{ConstDecoder, QueryFinder}; +use salvo::jwt_auth::{ConstDecoder, CookieFinder, HeaderFinder, QueryFinder}; use salvo::prelude::*; use serde::{Deserialize, Serialize}; use time::{Duration, OffsetDateTime}; @@ -18,16 +18,16 @@ pub fn jwt_hoop() -> JwtAuth { CFG.jwt.jwt_secret.to_owned().as_bytes(), )) .finders(vec![ - // Box::new(HeaderFinder::new()), + Box::new(HeaderFinder::new()), Box::new(QueryFinder::new("token")), - // Box::new(CookieFinder::new("jwt_token")), + Box::new(CookieFinder::new("jwt_token")), ]) - .force_passed(true); + .force_passed(false); auth_handler } #[allow(dead_code)] -pub fn get_token(username: String, user_id: String) -> Result { +pub fn get_token(username: String, user_id: String) -> Result<(String, i64)> { let exp = OffsetDateTime::now_utc() + Duration::seconds(CFG.jwt.jwt_exp); let claim = JwtClaims { username, @@ -39,5 +39,5 @@ pub fn get_token(username: String, user_id: String) -> Result { &claim, &EncodingKey::from_secret(CFG.jwt.jwt_secret.as_bytes()), )?; - Ok(token) + Ok((token, exp.unix_timestamp())) } diff --git a/src/template/src/middleware/mod.rs b/src/template/src/middleware/mod.rs index 417233c..5802505 100644 --- a/src/template/src/middleware/mod.rs +++ b/src/template/src/middleware/mod.rs @@ -1 +1,2 @@ +pub mod handle404; pub mod jwt; diff --git a/src/template/src/models/mod.rs b/src/template/src/models/mod.rs new file mode 100644 index 0000000..22d12a3 --- /dev/null +++ b/src/template/src/models/mod.rs @@ -0,0 +1 @@ +pub mod user; diff --git a/src/template/src/models/user.rs b/src/template/src/models/user.rs new file mode 100644 index 0000000..025bf41 --- /dev/null +++ b/src/template/src/models/user.rs @@ -0,0 +1,9 @@ +use serde::Serialize; +use sqlx::FromRow; + +#[derive(FromRow, Serialize, Debug)] +pub struct User { + pub id: String, + pub username: String, + pub password: String, +} diff --git a/src/template/src/routers/demo.hbs b/src/template/src/routers/demo.hbs new file mode 100644 index 0000000..f599705 --- /dev/null +++ b/src/template/src/routers/demo.hbs @@ -0,0 +1,48 @@ +use askama::Template; +use salvo::{endpoint, writing::Text, Request, Response}; + +use crate::app_error::AppResult; +{{#if is_web_site}} + +#[derive(Template)] +#[template(path = "hello.html")] +struct HelloTemplate<'a> { + name: &'a str, +} + +#[endpoint] +async fn hello(req: &mut Request, res: &mut Response)->AppResult<()>{ + let hello_tmpl = HelloTemplate { + name: req.param::<&str>("name").unwrap_or("World"), + }; + res.render(Text::Html(hello_tmpl.render().unwrap())); + Ok(()) +} +{{else}} +#[endpoint] +async fn hello() -> AppResult<&'static str> { + Ok("Hello World from salvo") +} +{{/if}} +mod tests { + use salvo::prelude::*; + use salvo::test::{ResponseExt, TestClient}; + + use crate::config::CFG; + + #[tokio::test] + async fn test_hello_world() { + let service = Service::new(crate::routers::router()); + + let content = TestClient::get(format!( + "http://{}", + &CFG.server.address.replace("0.0.0.0", "127.0.0.1") + )) + .send(&service) + .await + .take_string() + .await + .unwrap(); + assert_eq!(content, "Hello World from salvo"); + } +} diff --git a/src/template/src/routers/mod.hbs b/src/template/src/routers/mod.hbs new file mode 100644 index 0000000..a52ae4d --- /dev/null +++ b/src/template/src/routers/mod.hbs @@ -0,0 +1,43 @@ +use crate::middleware::jwt::jwt_hoop; +use salvo::{ + prelude::{CatchPanic, Logger, OpenApi, SwaggerUi}, + Router, +}; + +use self::{ + demo::hello, +{{#if need_db_conn}} + user::{ + delete_user, get_users, login_page, post_add_user, post_login, post_update_user, + user_list_page, + }, +{{/if}} +}; +pub mod demo; +pub mod user; + +pub fn router() -> Router { + let router = Router::new() + .hoop(Logger::new()) + .hoop(CatchPanic::new()) + .get(hello) + .push(Router::with_path("login").get(login_page).post(post_login)) + .push(user_router().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 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 diff --git a/src/template/src/routers/user.hbs b/src/template/src/routers/user.hbs new file mode 100644 index 0000000..eec74bf --- /dev/null +++ b/src/template/src/routers/user.hbs @@ -0,0 +1,108 @@ +use askama::Template; +use salvo::{ + endpoint, + http::cookie::Cookie, + oapi::extract::{FormBody, JsonBody}, + writing::{Redirect, Text}, + Request, Response, +}; + +use crate::{ + app_error::AppResult, + app_response::{ErrRes, Res}, + dtos::user::{ + UserAddRequest, UserDeleteRequest, UserLoginRequest, UserResponse, UserUpdateRequest, + }, + services::user, +}; +{{#if is_web_site}} +#[derive(Template)] +#[template(path = "login.html")] +struct LoginTemplate {} + +#[endpoint] +pub async fn login_page(res: &mut Response) -> AppResult<()> { + 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) { + 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), + } +} +{{#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; + 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); + //Res::with_data(data).into_response(res)}, + res.render(Redirect::other("/user")); + } + Err(e) => ErrRes::with_err(&e.to_string()).into_response(res), + } +{{else}} +#[endpoint] +pub async fn post_login(req: FormBody, res: &mut Response) { + let result = user::login(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), + } +} +{{/if}} diff --git a/src/template/src/services/mod.rs b/src/template/src/services/mod.rs new file mode 100644 index 0000000..22d12a3 --- /dev/null +++ b/src/template/src/services/mod.rs @@ -0,0 +1 @@ +pub mod user; diff --git a/src/template/src/services/user.hbs b/src/template/src/services/user.hbs new file mode 100644 index 0000000..004ad1c --- /dev/null +++ b/src/template/src/services/user.hbs @@ -0,0 +1,124 @@ +use crate::{ + app_error::AppResult, + db::DB, + dtos::user::{ + UserAddRequest, UserDeleteRequest, UserLoginRequest, UserLoginResponse, UserResponse, + UserUpdateRequest, + }, + middleware::jwt::get_token, + models::user::User, + utils::rand_utils, +}; +use uuid::Uuid; +{{#if is_sqlx}} +pub async fn add_user(req: UserAddRequest) -> AppResult { + let db = DB.get().ok_or(anyhow::anyhow!("数据库连接失败"))?; + let id = Uuid::new_v4().to_string(); + let hash_password = rand_utils::hash_password(req.password).await?; + let _ = sqlx::query!( + r#" + INSERT INTO users (id, username, password) + VALUES ($1, $2, $3) + "#, + id, + req.username, + hash_password, + ) + .execute(db) + .await?; + + Ok(UserResponse { + id, + username: req.username, + }) +} + +pub async fn login(req: UserLoginRequest) -> AppResult { + let db = DB.get().ok_or(anyhow::anyhow!("数据库连接失败"))?; + let user = sqlx::query_as!( + User, + r#" + SELECT id, username, password FROM users + WHERE username = $1 + "#, + req.username + ) + .fetch_optional(db) + .await?; + if user.is_none() { + return Err(anyhow::anyhow!("用户不存在").into()); + } + let user = user.unwrap(); + if rand_utils::verify_password(req.password, user.password) + .await + .is_err() + { + return Err(anyhow::anyhow!("密码不正确").into()); + } + let (token, exp) = get_token(user.username.clone(), user.id.clone())?; + let res = UserLoginResponse { + id: user.id, + username: user.username, + token, + exp, + }; + Ok(res) +} + +pub async fn update_user(req: UserUpdateRequest) -> AppResult { + let db = DB.get().ok_or(anyhow::anyhow!("数据库连接失败"))?; + let hash_password = rand_utils::hash_password(req.password).await?; + let _ = sqlx::query!( + r#" + UPDATE users + SET username = $1, password = $2 + WHERE id = $3 + "#, + req.username, + hash_password, + req.id, + ) + .execute(db) + .await?; + + Ok(UserResponse { + id: req.id, + username: req.username, + }) +} + +pub async fn delete_user(req: UserDeleteRequest) -> AppResult<()> { + let db = DB.get().ok_or(anyhow::anyhow!("数据库连接失败"))?; + sqlx::query!( + r#" + DELETE FROM users + WHERE id = $1 + "#, + req.id, + ) + .execute(db) + .await?; + + Ok(()) +} + +pub async fn users() -> AppResult> { + let db = DB.get().ok_or(anyhow::anyhow!("数据库连接失败"))?; + let users = sqlx::query_as!( + User, + r#" + SELECT id, username, password FROM users + "#, + ) + .fetch_all(db) + .await?; + let res = users + .into_iter() + .map(|user| UserResponse { + id: user.id, + username: user.username, + }) + .collect::>(); + Ok(res) +} +{{/if}} \ No newline at end of file diff --git a/src/template/templates/login.html b/src/template/templates/login.html new file mode 100644 index 0000000..502560f --- /dev/null +++ b/src/template/templates/login.html @@ -0,0 +1,111 @@ + + + + + + + salvo + + +
+
+
+

+ login +

+
+
+ +
+
+ + +
+
+ + +
+
+
+ +
+
+
+
+ + + + + + + + + diff --git a/src/template/templates/user_list.html b/src/template/templates/user_list.html new file mode 100644 index 0000000..939cb2a --- /dev/null +++ b/src/template/templates/user_list.html @@ -0,0 +1,96 @@ +
+
+
+
+

+ 用户列表 +

+

名称

+
+
+
+
+
+ + + + + + + + + + {% for user in users %} + + + + + + {% endfor %} + + +
+ Name + + Delete + Delete +
+ {{user.username}} + 删除删除 +
+
+
+
+
+
diff --git a/src/template/templates/user_list_page.html b/src/template/templates/user_list_page.html new file mode 100644 index 0000000..d7f65dc --- /dev/null +++ b/src/template/templates/user_list_page.html @@ -0,0 +1,40 @@ + + + + + + + salvo + + {% include "user_list.html" %} + + + + + + + + diff --git a/src/utils/create_project.rs b/src/utils/create_project.rs index ea968ae..b145e1f 100644 --- a/src/utils/create_project.rs +++ b/src/utils/create_project.rs @@ -13,7 +13,7 @@ use std::{ slice, }; -use super::{print_util, restricted_names, warning, get_selection::{get_user_selected, UserSelected, TemplateType}}; +use super::{print_util, restricted_names, warning, get_selection::{get_user_selected, UserSelected, TemplateType, DbConnectionType, DbType}}; pub fn create_project(project: Project) -> Result<()> { check_name(&project.project_name)?; @@ -39,9 +39,14 @@ pub fn create_project(project: Project) -> Result<()> { Ok(()) } -fn write_project_file(project_path: &Path, config: UserSelected, project: Project) -> Result<()> { +fn write_project_file(project_path: &Path, user_selected: UserSelected, project: Project) -> Result<()> { let handlebars = Handlebars::new(); - let is_web_site = config.template_type == TemplateType::SalvoWebSite; + let is_web_site = user_selected.template_type == TemplateType::SalvoWebSite; + let need_db_conn=user_selected.db_conn_type!=DbConnectionType::Nothing; + let is_sqlx=user_selected.db_conn_type==DbConnectionType::Sqlx; + let is_mysql = user_selected.db_type == DbType::Mysql; + let is_postgres = user_selected.db_type == DbType::Postgres; + let is_sqlite = user_selected.db_type == DbType::Sqlite; let data = json!({ "project_name": project.project_name, "dependencies": { @@ -64,6 +69,11 @@ fn write_project_file(project_path: &Path, config: UserSelected, project: Projec "tracing": "0.1" }, "is_web_site":is_web_site, + "need_db_conn":need_db_conn, + "is_sqlx":is_sqlx, + "is_mysql":is_mysql, + "is_postgres":is_postgres, + "is_sqlite":is_sqlite, "main_log_message":t!("main_log_message"), "config_error_no_exits":t!("config_error_no_exits"), "config_error_read":t!("config_error_read"), @@ -89,10 +99,10 @@ fn write_project_file(project_path: &Path, config: UserSelected, project: Projec let config_rendered = handlebars.render_template(config_template, &data)?; let mut config_file = File::create(src_path.join("config.rs"))?; config_file.write_all(config_rendered.as_bytes())?; - let app_error_rs = include_bytes!("../template/src/app_error.rs"); + let app_error_template = include_str!("../template/src/app_error.hbs"); + let app_error_rendered = handlebars.render_template(app_error_template, &data)?; let mut app_error_file = File::create(src_path.join("app_error.rs"))?; - app_error_file.write_all(app_error_rs)?; - + app_error_file.write_all(app_error_rendered.as_bytes())?; let middleware_path = src_path.join("middleware"); std::fs::create_dir_all(&middleware_path)?; let jwt_bytes = include_bytes!("../template/src/middleware/jwt.rs"); From 56549e617643c8316e7e77616735e887a6ec9984 Mon Sep 17 00:00:00 2001 From: Fankai Liu Date: Wed, 4 Oct 2023 23:23:26 +0800 Subject: [PATCH 05/19] add some comment --- src/template/src/app_response.hbs | 53 +++++++++ src/template/src/config_template.hbs | 9 ++ src/template/src/db.hbs | 47 ++++++++ src/template/src/services/{mod.rs => mod.hbs} | 0 src/utils/create_project.rs | 104 ++++++++++++++++-- src/utils/get_selection.rs | 38 +++---- src/utils/mod.rs | 2 +- 7 files changed, 224 insertions(+), 29 deletions(-) create mode 100644 src/template/src/app_response.hbs create mode 100644 src/template/src/db.hbs rename src/template/src/services/{mod.rs => mod.hbs} (100%) diff --git a/src/template/src/app_response.hbs b/src/template/src/app_response.hbs new file mode 100644 index 0000000..8c72fa5 --- /dev/null +++ b/src/template/src/app_response.hbs @@ -0,0 +1,53 @@ +use salvo::{prelude::StatusCode, writing::Json, Response}; +use serde::Serialize; + +#[derive(Debug, Serialize, Default)] +pub struct Res { + pub code: i32, + pub data: T, + pub msg: String, +} + +#[derive(Debug, Serialize, Default)] +pub struct ErrRes { + pub code: i32, + pub msg: String, +} + +impl Res { + pub fn with_data(data: T) -> Self { + Self { + code: 200, + data, + msg: "success".to_string(), + } + } + #[allow(dead_code)] + pub fn with_data_msg(data: T, msg: &str) -> Self { + Self { + code: 200, + data, + msg: msg.to_string(), + } + } +} + +impl ErrRes { + pub fn with_err(err: &str) -> Self { + Self { + code: 500, + msg: err.to_string(), + } + } +} +impl Res { + pub fn into_response(self, res: &mut Response) { + res.render(Json(self)); + } +} + +impl ErrRes { + pub fn into_response(self, res: &mut Response) { + res.stuff(StatusCode::INTERNAL_SERVER_ERROR, Json(self)); + } +} diff --git a/src/template/src/config_template.hbs b/src/template/src/config_template.hbs index 8988709..c9f2626 100644 --- a/src/template/src/config_template.hbs +++ b/src/template/src/config_template.hbs @@ -6,6 +6,9 @@ use std::{fs::File, io::Read, path::Path}; pub struct Configs { pub server: Server, pub log: Log, + {{#if need_db_conn}} + pub database: DataBase, + {{/if}} pub cert: Cert, pub jwt: Jwt, } @@ -16,6 +19,12 @@ pub struct Server { pub address: String, pub ssl: bool, } +{{#if need_db_conn}} +#[derive(Debug, Deserialize)] +pub struct DataBase { + pub database_url: String, +} +{{/if}} #[derive(Debug, Deserialize)] pub struct Log { diff --git a/src/template/src/db.hbs b/src/template/src/db.hbs new file mode 100644 index 0000000..c8de91c --- /dev/null +++ b/src/template/src/db.hbs @@ -0,0 +1,47 @@ +{{#if is_sqlx}} +{{#if is_mysql}} +use sqlx::MySqlPool; +{{/if}} +{{#if is_postgres}} +use sqlx::PgPool; +{{/if}} +{{#if is_sqlite}} +use sqlx::SqlitePool; +{{/if}} +{{/if}} +use tokio::sync::OnceCell; + +use crate::config::CFG; +{{#if is_sqlx}} +{{#if is_sqlite}} +pub static DB: OnceCell = OnceCell::const_new(); +{{/if}} +{{#if is_postgres}} +pub static DB: OnceCell = OnceCell::const_new(); +{{/if}} +{{#if is_mysql}} +pub static DB: OnceCell = OnceCell::const_new(); +{{/if}} +{{/if}} +pub async fn init_db_conn() { + DB.get_or_init(|| async { + {{#if is_sqlx}} + {{#if is_sqlite}} + SqlitePool::connect(&CFG.database.database_url) + .await + .expect("数据库打开失败") + {{/if}} + {{#if is_postgres}} + PgPool::connect(&CFG.database.database_url) + .await + .expect("数据库打开失败") + {{/if}} + {{#if is_mysql}} + MySqlPool::connect(&CFG.database.database_url) + .await + .expect("数据库打开失败") + {{/if}} + {{/if}} + }) + .await; +} diff --git a/src/template/src/services/mod.rs b/src/template/src/services/mod.hbs similarity index 100% rename from src/template/src/services/mod.rs rename to src/template/src/services/mod.hbs diff --git a/src/utils/create_project.rs b/src/utils/create_project.rs index b145e1f..fca0fd3 100644 --- a/src/utils/create_project.rs +++ b/src/utils/create_project.rs @@ -13,14 +13,20 @@ use std::{ slice, }; -use super::{print_util, restricted_names, warning, get_selection::{get_user_selected, UserSelected, TemplateType, DbConnectionType, DbType}}; +use super::{ + get_selection::{get_user_selected, DbConnectionType, DbType, TemplateType, UserSelected}, + print_util, restricted_names, warning, +}; pub fn create_project(project: Project) -> Result<()> { check_name(&project.project_name)?; let project_name = &project.project_name; let project_path = Path::new(project_name); if project_path.exists() { - anyhow::bail!(t!("error_project_path_exist",path=project_path.to_string_lossy())) + anyhow::bail!(t!( + "error_project_path_exist", + path = project_path.to_string_lossy() + )) } check_path(project_path)?; @@ -39,11 +45,15 @@ pub fn create_project(project: Project) -> Result<()> { Ok(()) } -fn write_project_file(project_path: &Path, user_selected: UserSelected, project: Project) -> Result<()> { +fn write_project_file( + project_path: &Path, + user_selected: UserSelected, + project: Project, +) -> Result<()> { let handlebars = Handlebars::new(); let is_web_site = user_selected.template_type == TemplateType::SalvoWebSite; - let need_db_conn=user_selected.db_conn_type!=DbConnectionType::Nothing; - let is_sqlx=user_selected.db_conn_type==DbConnectionType::Sqlx; + let need_db_conn = user_selected.db_conn_type != DbConnectionType::Nothing; + let is_sqlx = user_selected.db_conn_type == DbConnectionType::Sqlx; let is_mysql = user_selected.db_type == DbType::Mysql; let is_postgres = user_selected.db_type == DbType::Postgres; let is_sqlite = user_selected.db_type == DbType::Sqlite; @@ -83,62 +93,139 @@ fn write_project_file(project_path: &Path, user_selected: UserSelected, project: std::fs::create_dir_all(project_path)?; let src_path = project_path.join("src"); + //src std::fs::create_dir_all(&src_path)?; - + //src/main.rs let main_file_path = src_path.join("main.rs"); let main_template = include_str!("../template/src/main_template.hbs"); let main_rendered = handlebars.render_template(main_template, &data)?; let mut main_file = File::create(main_file_path)?; main_file.write_all(main_rendered.as_bytes())?; + //src/Cargo.toml let cargo_file_path = project_path.join("Cargo.toml"); let cargo_template = include_str!("../template/src/cargo_template.hbs"); let cargo_rendered = handlebars.render_template(cargo_template, &data)?; let mut cargo_file = File::create(cargo_file_path)?; cargo_file.write_all(cargo_rendered.as_bytes())?; + //src/config.rs let config_template = include_str!("../template/src/config_template.hbs"); let config_rendered = handlebars.render_template(config_template, &data)?; let mut config_file = File::create(src_path.join("config.rs"))?; config_file.write_all(config_rendered.as_bytes())?; + //src/app_error.rs let app_error_template = include_str!("../template/src/app_error.hbs"); let app_error_rendered = handlebars.render_template(app_error_template, &data)?; let mut app_error_file = File::create(src_path.join("app_error.rs"))?; app_error_file.write_all(app_error_rendered.as_bytes())?; + if need_db_conn { + //src/db.rs + let db_template = include_str!("../template/src/db.hbs"); + let db_rendered = handlebars.render_template(db_template, &data)?; + let mut db_file = File::create(src_path.join("db.rs"))?; + db_file.write_all(db_rendered.as_bytes())?; + //src/app_response.rs + let app_response_template = include_str!("../template/src/app_response.hbs"); + let app_response_rendered = handlebars.render_template(app_response_template, &data)?; + let mut app_response_file = File::create(src_path.join("app_response.rs"))?; + app_response_file.write_all(app_response_rendered.as_bytes())?; + } + //src/middleware let middleware_path = src_path.join("middleware"); std::fs::create_dir_all(&middleware_path)?; let jwt_bytes = include_bytes!("../template/src/middleware/jwt.rs"); let mut jwt_file = File::create(middleware_path.join("jwt.rs"))?; jwt_file.write_all(jwt_bytes)?; + //src/middleware/mod.rs let mod_bytes = include_bytes!("../template/src/middleware/mod.rs"); let mut mod_file = File::create(middleware_path.join("mod.rs"))?; mod_file.write_all(mod_bytes)?; + //src/middleware/handle404.rs + let handle404_template = include_str!("../template/src/middleware/handle404.hbs"); + let handle404_rendered = handlebars.render_template(handle404_template, &data)?; + let mut handle404_file = File::create(middleware_path.join("handle404.rs"))?; + handle404_file.write_all(handle404_rendered.as_bytes())?; + //config let config_path = project_path.join("config"); std::fs::create_dir_all(&config_path)?; + //config/config.toml let config_template = include_str!("../template/config/config.hbs"); - let config_toml_rendered = handlebars.render_template(config_template, &data)?; let mut config_file = File::create(config_path.join("config.toml"))?; config_file.write_all(config_toml_rendered.as_bytes())?; - + //config/certs let cert_path = config_path.join("certs"); std::fs::create_dir_all(&cert_path)?; + //config/certs/cert.pem let cert_template = include_str!("../template/config/certs/cert.pem"); let mut cert_file = File::create(cert_path.join("cert.pem"))?; cert_file.write_all(cert_template.as_bytes())?; + //config/certs/key.pem let key_path = cert_path.join("key.pem"); let key_template = include_str!("../template/config/certs/key.pem"); let mut key_file = File::create(key_path)?; key_file.write_all(key_template.as_bytes())?; if is_web_site { + //template let template_path = project_path.join("template"); std::fs::create_dir_all(&template_path)?; + //template/hello.html let hello_html_template = include_bytes!("../template/templates/hello.html"); let mut hello_html_file = File::create(template_path.join("hello.html"))?; hello_html_file.write_all(hello_html_template)?; + //template/handle_404.html let handle_404_template = include_bytes!("../template/templates/404.html"); let mut handle_404_file = File::create(template_path.join("handle_404.html"))?; handle_404_file.write_all(handle_404_template)?; + if need_db_conn { + //template/login.html + let login_html_template = include_bytes!("../template/templates/login.html"); + let mut login_html_file = File::create(template_path.join("login.html"))?; + login_html_file.write_all(login_html_template)?; + //template/user_list_page + let user_list_page_template = + include_bytes!("../template/templates/user_list_page.html"); + let mut user_list_page_file = File::create(template_path.join("user_list_page.html"))?; + user_list_page_file.write_all(user_list_page_template)?; + } } + //src/router + let router_path = src_path.join("routers"); + std::fs::create_dir_all(&router_path)?; + //src/router/mod.rs + let router_mod_template = include_str!("../template/src/routers/mod.hbs"); + let router_mod_rendered = handlebars.render_template(router_mod_template, &data)?; + let mut router_mod_file = File::create(router_path.join("mod.rs"))?; + router_mod_file.write_all(router_mod_rendered.as_bytes())?; + //src/router/demo.rs + let router_demo_template = include_str!("../template/src/routers/demo.hbs"); + let router_demo_rendered = handlebars.render_template(router_demo_template, &data)?; + let mut router_demo_file = File::create(router_path.join("demo.rs"))?; + router_demo_file.write_all(router_demo_rendered.as_bytes())?; + if need_db_conn { + //src/router/user.rs + let router_user_template = include_str!("../template/src/routers/user.hbs"); + let router_user_rendered = handlebars.render_template(router_user_template, &data)?; + let mut router_user_file = File::create(router_path.join("user.rs"))?; + router_user_file.write_all(router_user_rendered.as_bytes())?; + } + + if need_db_conn { + //src/services + let services_path = src_path.join("services"); + std::fs::create_dir_all(&services_path)?; + //src/services/mod.rs + let services_mod_template = include_str!("../template/src/services/mod.hbs"); + let services_mod_rendered = handlebars.render_template(services_mod_template, &data)?; + let mut services_mod_file = File::create(services_path.join("mod.rs"))?; + services_mod_file.write_all(services_mod_rendered.as_bytes())?; + //src/services/user.rs + let services_user_template = include_str!("../template/src/services/user.hbs"); + let services_user_rendered = handlebars.render_template(services_user_template, &data)?; + let mut services_user_file = File::create(services_path.join("user.rs"))?; + services_user_file.write_all(services_user_rendered.as_bytes())?; + } + Ok(()) } @@ -218,4 +305,3 @@ fn _create_dir_all(p: &Path) -> Result<()> { .with_context(|| format!("failed to create directory `{}`", p.display()))?; Ok(()) } - diff --git a/src/utils/get_selection.rs b/src/utils/get_selection.rs index e29a7cc..20ee84a 100644 --- a/src/utils/get_selection.rs +++ b/src/utils/get_selection.rs @@ -1,6 +1,6 @@ use anyhow::Result; +use dialoguer::{console::Style, theme::ColorfulTheme, Select}; use rust_i18n::t; -use dialoguer::{theme::ColorfulTheme, Select, console::Style}; #[derive(Debug)] pub struct UserSelected { @@ -9,7 +9,6 @@ pub struct UserSelected { pub db_conn_type: DbConnectionType, } - pub fn get_user_selected() -> Result> { let theme = ColorfulTheme { defaults_style: Style::new().blue(), @@ -47,20 +46,17 @@ pub fn get_user_selected() -> Result> { .items(&db_conn_types[..]) .interact()?; - let db_conn_type = match db_conn_type_selection { - 0 => DbConnectionType::Sqlx, - 1 => DbConnectionType::Diesel, - 2 => DbConnectionType::SeaOrm, - 3 => DbConnectionType::Rbatis, - 4 => DbConnectionType::Nothing, - _ => anyhow::bail!("Invalid db connection type selection"), - }; - + let db_conn_type = match db_conn_type_selection { + 0 => DbConnectionType::Sqlx, + 1 => DbConnectionType::Diesel, + 2 => DbConnectionType::SeaOrm, + 3 => DbConnectionType::Rbatis, + 4 => DbConnectionType::Nothing, + _ => anyhow::bail!("Invalid db connection type selection"), + }; let db_types = &[ - "sqlite", - "mysql", - "postgres", + "sqlite", "mysql", "postgres", // "custom", ]; let db_type_selection = Select::with_theme(&theme) @@ -75,7 +71,11 @@ pub fn get_user_selected() -> Result> { _ => anyhow::bail!("Invalid db type selection"), }; - Ok(Some(UserSelected { template_type, db_type, db_conn_type })) + Ok(Some(UserSelected { + template_type, + db_type, + db_conn_type, + })) } #[derive(Debug, PartialEq)] pub enum TemplateType { @@ -84,17 +84,17 @@ pub enum TemplateType { } #[derive(Debug, PartialEq)] -pub enum DbType { +pub enum DbType { Sqlite, Mysql, Postgres, } -#[derive(Debug,PartialEq)] -pub enum DbConnectionType { +#[derive(Debug, PartialEq)] +pub enum DbConnectionType { Sqlx, Diesel, SeaOrm, Rbatis, Nothing, -} \ No newline at end of file +} diff --git a/src/utils/mod.rs b/src/utils/mod.rs index 27ccd30..983ff57 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -1,6 +1,6 @@ pub mod create_project; +pub mod get_selection; mod print_util; mod restricted_names; -pub mod get_selection; pub use create_project::create_project; pub use print_util::{error, print_logo, success, warning}; From 398eb2cb95c1dfc619ad317dcdf673ea6ec0d67a Mon Sep 17 00:00:00 2001 From: Fankai Liu Date: Thu, 5 Oct 2023 21:26:34 +0800 Subject: [PATCH 06/19] add some template --- src/template/.env.hbs | 9 ++ src/template/data/demo.db | Bin 0 -> 24576 bytes src/template/data/demo.db-shm | Bin 0 -> 32768 bytes src/template/data/demo.db-wal | 0 src/template/src/dtos/{mod.rs => mod.hbs} | 0 src/template/src/dtos/{user.rs => user.hbs} | 0 src/template/src/main_template.hbs | 12 +- src/template/src/middleware/handle404.hbs | 26 ---- src/template/src/middleware/handle_404.hbs | 29 ++++ src/template/src/middleware/mod.rs | 2 +- src/template/src/models/{mod.rs => mod.hbs} | 0 src/template/src/models/{user.rs => user.hbs} | 0 src/template/src/routers/demo.hbs | 6 +- src/template/src/routers/mod.hbs | 12 ++ src/template/src/routers/user.hbs | 3 +- .../{utils/mod.rs => src/utils/mod.hbs} | 0 .../utils/rand_utils.hbs} | 0 src/utils/create_project.rs | 124 ++++++++++++++++-- src/utils/get_selection.rs | 8 +- 19 files changed, 182 insertions(+), 49 deletions(-) create mode 100644 src/template/.env.hbs create mode 100644 src/template/data/demo.db create mode 100644 src/template/data/demo.db-shm create mode 100644 src/template/data/demo.db-wal rename src/template/src/dtos/{mod.rs => mod.hbs} (100%) rename src/template/src/dtos/{user.rs => user.hbs} (100%) delete mode 100644 src/template/src/middleware/handle404.hbs create mode 100644 src/template/src/middleware/handle_404.hbs rename src/template/src/models/{mod.rs => mod.hbs} (100%) rename src/template/src/models/{user.rs => user.hbs} (100%) rename src/template/{utils/mod.rs => src/utils/mod.hbs} (100%) rename src/template/{utils/rand_utils.rs => src/utils/rand_utils.hbs} (100%) diff --git a/src/template/.env.hbs b/src/template/.env.hbs new file mode 100644 index 0000000..ffe8cd2 --- /dev/null +++ b/src/template/.env.hbs @@ -0,0 +1,9 @@ +{{#if is_postgres}} +DATABASE_URL=postgresql://liufankai:1@localhost/salvo_demo +{{/if}} +{{#if is_sqlx}} +DATABASE_URL="sqlite:data/demo.db" +{{/if}} +{{#if is_mysql}} +DATABASE_URL="mysql://root:981109@localhost/salvo_demo" +{{/if}} \ No newline at end of file diff --git a/src/template/data/demo.db b/src/template/data/demo.db new file mode 100644 index 0000000000000000000000000000000000000000..e983043eec2cd2ab0bad65f2036bbe85564a1055 GIT binary patch literal 24576 zcmeI&J#W)M7zc1WX&MsL23RsxI;2Q6qDHnG_f3kxX$=INq)lUJ%U~SaX{_e0vC~$C zr4@n!vGEBQI+Tft4*)YWNJua-5;DiENq5JFK@z|Dhh?xk*dpjU8@^l#@!od&2&l}|&l?^I~$5jFfc_HyXa z@ZDkIsBqLn00Izz00bZa0SG_<0uVUy0y`Ij;dnew?+T7qu9+R%Y}?*$V74fUC5e>8 zj4TnaDmoe^ZnBK--c2QGsYK?B*_>EhCO4&Jk}s4sm-CAvHj@4|~{pG+Rnf_MdBW<{jZfBCo zUd!eWDfz24Oj~bTt?mH6W6#8Iwi>ph)oP}pcH6T#X|W{c<`00$oOD%G?siAlP1`1!LP3_q{6RmzRj-=*n%$|Bj9kd{wV7L{-s#Rtbu9PN{hEGna?#}F zz+illIqC}ZW2jtzP0+ogPId39Ave>pJ<8pXAOHafKmY;|fB*y_009U<00Iy=R)Hlt z9Ey)eYQb|A^Vv7Ydtbt{{5Zq049gRipJAtG*i>Pz7@OZ8xVU=%p;37K?#JgnDST)B z&5Q7*r@Pr#ruP2H`R$J%p5Gfuf7)Na9l3M*)!C@KE;{y=ayKLhKmY;|fB*y_009U< z00Izz00d5yKqMH7_^%TD@BeF5Z0$rX5Q~8T1Rwwb2tWV=5P$##AOHaf9GAe(IXa|_ z>=bmvU`=+4WrT8>V^XOU&y*)BlMG+hCRn3vu!10LSGC5ftu+!_d$rl%EhDj!=B5*M zcb5_-#+)=i)=G1Uw%)X5p;eeSL|&h?QY#% { } #[endpoint] -async fn hello(req: &mut Request, res: &mut Response)->AppResult<()>{ +pub async fn hello(req: &mut Request, res: &mut Response)->AppResult<()>{ let hello_tmpl = HelloTemplate { name: req.param::<&str>("name").unwrap_or("World"), }; @@ -20,7 +22,7 @@ async fn hello(req: &mut Request, res: &mut Response)->AppResult<()>{ } {{else}} #[endpoint] -async fn hello() -> AppResult<&'static str> { +pub async fn hello() -> AppResult<&'static str> { Ok("Hello World from salvo") } {{/if}} diff --git a/src/template/src/routers/mod.hbs b/src/template/src/routers/mod.hbs index a52ae4d..49c7bc7 100644 --- a/src/template/src/routers/mod.hbs +++ b/src/template/src/routers/mod.hbs @@ -7,22 +7,34 @@ use salvo::{ use self::{ demo::hello, {{#if need_db_conn}} +{{#if is_web_site}} user::{ delete_user, get_users, login_page, post_add_user, post_login, post_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 { let router = Router::new() .hoop(Logger::new()) .hoop(CatchPanic::new()) + {{#if need_db_conn}} .get(hello) .push(Router::with_path("login").get(login_page).post(post_login)) .push(user_router().hoop(jwt_hoop())); + {{else}} + .get(hello); + {{/if}} let doc = OpenApi::new("salvo web api", "0.0.1").merge_router(&router); router .push(doc.into_router("/api-doc/openapi.json")) diff --git a/src/template/src/routers/user.hbs b/src/template/src/routers/user.hbs index eec74bf..d7e0bd8 100644 --- a/src/template/src/routers/user.hbs +++ b/src/template/src/routers/user.hbs @@ -1,4 +1,6 @@ +{{#if is_web_site}} use askama::Template; +{{/if}} use salvo::{ endpoint, http::cookie::Cookie, @@ -6,7 +8,6 @@ use salvo::{ writing::{Redirect, Text}, Request, Response, }; - use crate::{ app_error::AppResult, app_response::{ErrRes, Res}, diff --git a/src/template/utils/mod.rs b/src/template/src/utils/mod.hbs similarity index 100% rename from src/template/utils/mod.rs rename to src/template/src/utils/mod.hbs diff --git a/src/template/utils/rand_utils.rs b/src/template/src/utils/rand_utils.hbs similarity index 100% rename from src/template/utils/rand_utils.rs rename to src/template/src/utils/rand_utils.hbs diff --git a/src/utils/create_project.rs b/src/utils/create_project.rs index fca0fd3..dfbf94e 100644 --- a/src/utils/create_project.rs +++ b/src/utils/create_project.rs @@ -33,11 +33,11 @@ pub fn create_project(project: Project) -> Result<()> { let config = get_user_selected()?; match config { Some(config) => { - // write_project_file(project_path, config, project.clone())?; + write_project_file(project_path, config, project.clone())?; - // init_git(project_path)?; + init_git(project_path)?; - // success(t!("create_success", project_name = project_name).replace(r"\n", "\n")); + success(t!("create_success", project_name = project_name).replace(r"\n", "\n")); } None => anyhow::bail!("cli quit!"), } @@ -57,7 +57,7 @@ fn write_project_file( let is_mysql = user_selected.db_type == DbType::Mysql; let is_postgres = user_selected.db_type == DbType::Postgres; let is_sqlite = user_selected.db_type == DbType::Sqlite; - let data = json!({ + let mut data = json!({ "project_name": project.project_name, "dependencies": { "anyhow": "1.0.75", @@ -90,6 +90,43 @@ fn write_project_file( "config_error_parse":t!("config_error_parse"), "config_error_read_failed":t!("config_error_read_failed"), }); + if is_sqlx { + // Add sqlx dependencies + let mut dependencies = data["dependencies"].clone(); + if is_mysql { + dependencies["sqlx"] = json!({ + "version": "0.7", + "features": ["runtime-tokio", "macros", "mysql"] + }); + } + if is_postgres { + dependencies["sqlx"] = json!({ + "version": "0.7", + "features": ["runtime-tokio", "macros", "postgres"] + }); + } + if is_sqlite { + dependencies["sqlx"] = json!({ + "version": "0.7", + "features": ["runtime-tokio", "macros", "sqlite"] + }); + } + //add uuid dependency + dependencies["uuid"] = json!({ + "version": "1.4.1", + "features": ["v4", "fast-rng", "macro-diagnostics"] + }); + //add rand dependency + dependencies["rand"] = json!({ + "version": "0.8.5", + }); + //add argon2 dependency + dependencies["argon2"] = json!({ + "version": "0.5.2", + }); + data["dependencies"] = dependencies; + } + std::fs::create_dir_all(project_path)?; let src_path = project_path.join("src"); @@ -123,12 +160,13 @@ fn write_project_file( let db_rendered = handlebars.render_template(db_template, &data)?; let mut db_file = File::create(src_path.join("db.rs"))?; db_file.write_all(db_rendered.as_bytes())?; - //src/app_response.rs - let app_response_template = include_str!("../template/src/app_response.hbs"); - let app_response_rendered = handlebars.render_template(app_response_template, &data)?; - let mut app_response_file = File::create(src_path.join("app_response.rs"))?; - app_response_file.write_all(app_response_rendered.as_bytes())?; } + //src/app_response.rs + let app_response_template = include_str!("../template/src/app_response.hbs"); + let app_response_rendered = handlebars.render_template(app_response_template, &data)?; + let mut app_response_file = File::create(src_path.join("app_response.rs"))?; + app_response_file.write_all(app_response_rendered.as_bytes())?; + //src/middleware let middleware_path = src_path.join("middleware"); std::fs::create_dir_all(&middleware_path)?; @@ -140,9 +178,9 @@ fn write_project_file( let mut mod_file = File::create(middleware_path.join("mod.rs"))?; mod_file.write_all(mod_bytes)?; //src/middleware/handle404.rs - let handle404_template = include_str!("../template/src/middleware/handle404.hbs"); + let handle404_template = include_str!("../template/src/middleware/handle_404.hbs"); let handle404_rendered = handlebars.render_template(handle404_template, &data)?; - let mut handle404_file = File::create(middleware_path.join("handle404.rs"))?; + let mut handle404_file = File::create(middleware_path.join("handle_404.rs"))?; handle404_file.write_all(handle404_rendered.as_bytes())?; //config @@ -166,8 +204,8 @@ fn write_project_file( let mut key_file = File::create(key_path)?; key_file.write_all(key_template.as_bytes())?; if is_web_site { - //template - let template_path = project_path.join("template"); + //templates + let template_path = project_path.join("templates"); std::fs::create_dir_all(&template_path)?; //template/hello.html let hello_html_template = include_bytes!("../template/templates/hello.html"); @@ -224,6 +262,66 @@ fn write_project_file( let services_user_rendered = handlebars.render_template(services_user_template, &data)?; let mut services_user_file = File::create(services_path.join("user.rs"))?; services_user_file.write_all(services_user_rendered.as_bytes())?; + //src/utils + let utils_path = src_path.join("utils"); + std::fs::create_dir_all(&utils_path)?; + //src/utils/mod.rs + let utils_mod_template = include_str!("../template/src/utils/mod.hbs"); + let utils_mod_rendered = handlebars.render_template(utils_mod_template, &data)?; + let mut utils_mod_file = File::create(utils_path.join("mod.rs"))?; + utils_mod_file.write_all(utils_mod_rendered.as_bytes())?; + + //src/utils/rand_utils.rs + let rand_utils_template = include_str!("../template/src/utils/rand_utils.hbs"); + let rand_utils_rendered = handlebars.render_template(rand_utils_template, &data)?; + let mut rand_utils_file = File::create(utils_path.join("rand_utils.rs"))?; + rand_utils_file.write_all(rand_utils_rendered.as_bytes())?; + + //src/dtos + let dtos_path = src_path.join("dtos"); + std::fs::create_dir_all(&dtos_path)?; + //src/dtos/mod.rs + let dtos_mod_template = include_str!("../template/src/dtos/mod.hbs"); + let dtos_mod_rendered = handlebars.render_template(dtos_mod_template, &data)?; + let mut dtos_mod_file = File::create(dtos_path.join("mod.rs"))?; + dtos_mod_file.write_all(dtos_mod_rendered.as_bytes())?; + + //src/dtos/user.rs + let dtos_user_template = include_str!("../template/src/dtos/user.hbs"); + let dtos_user_rendered = handlebars.render_template(dtos_user_template, &data)?; + let mut dtos_user_file = File::create(dtos_path.join("user.rs"))?; + dtos_user_file.write_all(dtos_user_rendered.as_bytes())?; + + //src/models + let models_path = src_path.join("models"); + std::fs::create_dir_all(&models_path)?; + //src/models/mod.rs + let models_mod_template = include_str!("../template/src/models/mod.hbs"); + let models_mod_rendered = handlebars.render_template(models_mod_template, &data)?; + let mut models_mod_file = File::create(models_path.join("mod.rs"))?; + models_mod_file.write_all(models_mod_rendered.as_bytes())?; + + //src/models/user.rs + let models_user_template = include_str!("../template/src/models/user.hbs"); + let models_user_rendered = handlebars.render_template(models_user_template, &data)?; + let mut models_user_file = File::create(models_path.join("user.rs"))?; + models_user_file.write_all(models_user_rendered.as_bytes())?; + + if is_sqlx { + if is_sqlite { + //data + let data_path = project_path.join("data"); + std::fs::create_dir_all(&data_path)?; + //data/demo.db + let demo_db_bytes= include_bytes!("../template/data/demo.db"); + let mut demo_db_file = File::create(data_path.join("demo.db"))?; + } + //.env + let env_template = include_str!("../template/.env.hbs"); + let env_rendered = handlebars.render_template(env_template, &data)?; + let mut env_file = File::create(project_path.join(".env"))?; + env_file.write_all(env_rendered.as_bytes())?; + } } Ok(()) diff --git a/src/utils/get_selection.rs b/src/utils/get_selection.rs index 20ee84a..826c018 100644 --- a/src/utils/get_selection.rs +++ b/src/utils/get_selection.rs @@ -54,7 +54,13 @@ pub fn get_user_selected() -> Result> { 4 => DbConnectionType::Nothing, _ => anyhow::bail!("Invalid db connection type selection"), }; - + if db_conn_type == DbConnectionType::Nothing { + return Ok(Some(UserSelected { + template_type, + db_type: DbType::Sqlite, + db_conn_type, + })); + } let db_types = &[ "sqlite", "mysql", "postgres", // "custom", From 021c2da4adc6dc03c6a56c026a8798377fdba2d3 Mon Sep 17 00:00:00 2001 From: Fankai Liu Date: Thu, 5 Oct 2023 22:31:47 +0800 Subject: [PATCH 07/19] add template --- src/template/config/config.hbs | 12 ++++++++++++ src/template/migrations/20231001143156_users.sql | 6 ++++++ src/template/src/routers/mod.hbs | 4 ++++ src/template/src/routers/user.hbs | 2 +- src/utils/create_project.rs | 14 +++++++++++++- 5 files changed, 36 insertions(+), 2 deletions(-) create mode 100644 src/template/migrations/20231001143156_users.sql diff --git a/src/template/config/config.hbs b/src/template/config/config.hbs index 3336df4..2ef4fdf 100644 --- a/src/template/config/config.hbs +++ b/src/template/config/config.hbs @@ -2,6 +2,18 @@ name = "{{project_name}}" address = "0.0.0.0:5800" ssl = false +{{#if need_db_conn}} +[database] +{{#if is_sqlite}} +database_url= "sqlite:data/demo.db" +{{/if}} +{{#if is_postgres}} +database_url="postgresql://liufankai:1@localhost/salvo_demo" +{{/if}} +{{#if is_mysql}} +database_url="postgresql://liufankai:1@localhost/salvo_demo" +{{/if}} +{{/if}} [jwt] jwt_secret = "secret" jwt_exp = 6000 diff --git a/src/template/migrations/20231001143156_users.sql b/src/template/migrations/20231001143156_users.sql new file mode 100644 index 0000000..2e17ad1 --- /dev/null +++ b/src/template/migrations/20231001143156_users.sql @@ -0,0 +1,6 @@ +CREATE TABLE IF NOT EXISTS users +( + id TEXT PRIMARY KEY NOT NULL, + username VARCHAR(255) NOT NULL UNIQUE, + password VARCHAR(511) NOT NULL +); \ No newline at end of file diff --git a/src/template/src/routers/mod.hbs b/src/template/src/routers/mod.hbs index 49c7bc7..54a5bda 100644 --- a/src/template/src/routers/mod.hbs +++ b/src/template/src/routers/mod.hbs @@ -30,7 +30,11 @@ pub fn router() -> Router { .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); diff --git a/src/template/src/routers/user.hbs b/src/template/src/routers/user.hbs index d7e0bd8..3930e69 100644 --- a/src/template/src/routers/user.hbs +++ b/src/template/src/routers/user.hbs @@ -92,11 +92,11 @@ pub async fn post_login(req: FormBody, res: &mut Response) { .http_only(true) .finish(); res.add_cookie(cookie); - //Res::with_data(data).into_response(res)}, res.render(Redirect::other("/user")); } Err(e) => ErrRes::with_err(&e.to_string()).into_response(res), } +} {{else}} #[endpoint] pub async fn post_login(req: FormBody, res: &mut Response) { diff --git a/src/utils/create_project.rs b/src/utils/create_project.rs index dfbf94e..01e9670 100644 --- a/src/utils/create_project.rs +++ b/src/utils/create_project.rs @@ -66,7 +66,7 @@ fn write_project_file( "once_cell": "1.18.0", "salvo": { "version": "*", - "features": ["anyhow", "logging", "cors", "oapi", "jwt-auth", "rustls", "catch-panic"] + "features": ["anyhow", "logging", "cors", "oapi", "jwt-auth", "rustls", "catch-panic","cookie"] }, "serde": "1.0.188", "thiserror": "1.0.48", @@ -225,6 +225,10 @@ fn write_project_file( include_bytes!("../template/templates/user_list_page.html"); let mut user_list_page_file = File::create(template_path.join("user_list_page.html"))?; user_list_page_file.write_all(user_list_page_template)?; + //template/user_list.html + let user_list_html_template = include_bytes!("../template/templates/user_list.html"); + let mut user_list_html_file = File::create(template_path.join("user_list.html"))?; + user_list_html_file.write_all(user_list_html_template)?; } } //src/router @@ -315,7 +319,15 @@ fn write_project_file( //data/demo.db let demo_db_bytes= include_bytes!("../template/data/demo.db"); let mut demo_db_file = File::create(data_path.join("demo.db"))?; + demo_db_file.write_all(demo_db_bytes)?; } + //migrations + let migrations_path = project_path.join("migrations"); + std::fs::create_dir_all(&migrations_path)?; + //migrations/2021-10-20-000000_create_users_table/up.sql + let up_sql_bytes = include_bytes!("../template/migrations/20231001143156_users.sql"); + let mut up_sql_file = File::create(migrations_path.join("20231001143156_users.sql"))?; + up_sql_file.write_all(up_sql_bytes)?; //.env let env_template = include_str!("../template/.env.hbs"); let env_rendered = handlebars.render_template(env_template, &data)?; From 52c0dac0345ece5fb225fb7fa8857cbb61b684f7 Mon Sep 17 00:00:00 2001 From: Fankai Liu Date: Sat, 7 Oct 2023 23:06:53 +0800 Subject: [PATCH 08/19] add i18n --- locales/code_comment.yml | 112 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 112 insertions(+) diff --git a/locales/code_comment.yml b/locales/code_comment.yml index 3979f5a..97e0ddd 100644 --- a/locales/code_comment.yml +++ b/locales/code_comment.yml @@ -89,3 +89,115 @@ config_error_read_failed: th: อ่านไฟล์ล้มเหลว el: απέτυχε η ανάγνωση του αρχείου da: kunne ikke læse filen +user_does_not_exist: + en: User does not exist. + zh_CN: 用户不存在。 + zh_TW: 用戶不存在。 + fr: L'utilisateur n'existe pas. + ja: ユーザーが存在しません。 + es: El usuario no existe. + de: Benutzer existiert nicht. + ru: Пользователь не существует. + it: L'utente non esiste. + pt: O usuário não existe. + ko: 사용자가 존재하지 않습니다. + no: Brukeren eksisterer ikke. + is: Notandinn er ekki til. + uk: Користувача не існує. + th: ไม่มีผู้ใช้นี้ + el: Ο χρήστης δεν υπάρχει. + da: Brugeren findes ikke. + +incorrect_password: + en: Incorrect password. + zh_CN: 密码不正确。 + zh_TW: 密碼不正確。 + fr: Mot de passe incorrect. + ja: パスワードが間違っています。 + es: Contraseña incorrecta. + de: Falsches Passwort. + ru: Неверный пароль. + it: Password errata. + pt: Senha incorreta. + ko: 잘못된 비밀번호입니다. + no: Feil passord. + is: Rangt lykilorð. + uk: Невірний пароль. + th: รหัสผ่านไม่ถูกต้อง + el: Λάθος κωδικός πρόσβασης. + da: Forkert adgangskode. + +database_connection_failed: + en: Database connection failed. + zh_CN: 数据库连接失败。 + zh_TW: 數據庫連接失敗。 + fr: La connexion à la base de données a échoué. + ja: データベースへの接続に失敗しました。 + es: Falló la conexión a la base de datos. + de: Datenbankverbindung fehlgeschlagen. + ru: Сбой подключения к базе данных. + it: Connessione al database fallita. + pt: Falha na conexão com o banco de dados. + ko: 데이터베이스 연결에 실패했습니다. + no: Tilkobling til databasen mislyktes. + is: Tókst ekki að tengjast gagnagrunninum. + uk: Помилка підключення до бази даних. + th: การเชื่อมต่อฐานข้อมูลล้มเหลว + el: Η σύνδεση με τη βάση δεδομένων απέτυχε. + da: Databasetilkobling mislykkedes. +account: + en: Account + zh_CN: 账号 + zh_TW: 帳號 + fr: Compte + ja: アカウント + es: Cuenta + de: Konto + ru: Учетная запись + it: Account + pt: Conta + ko: 계정 + no: Konto + is: Reikningur + uk: Обліковий запис + th: บัญชี + el: Λογαριασμός + da: Konto + +password: + en: Password + zh_CN: 密码 + zh_TW: 密碼 + fr: Mot de passe + ja: パスワード + es: Contraseña + de: Passwort + ru: Пароль + it: Password + pt: Senha + ko: 비밀번호 + no: Passord + is: Lykilorð + uk: Пароль + th: รหัสผ่าน + el: Κωδικός + da: Adgangskode + +login: + en: Login + zh_CN: 登录 + zh_TW: 登入 + fr: Connexion + ja: ログイン + es: Iniciar sesión + de: Anmelden + ru: Войти + it: Accedi + pt: Login + ko: 로그인 + no: Logg inn + is: Innskráning + uk: Увійти + th: เข้าสู่ระบบ + el: Σύνδεση + da: Log ind From 0659dcc8424992afdb227c91982413a61ae7de30 Mon Sep 17 00:00:00 2001 From: Fankai Liu Date: Sun, 8 Oct 2023 22:43:30 +0800 Subject: [PATCH 09/19] add Multi-language support --- locales/code_comment.yml | 199 ++++++++++++++++++ src/template/src/db.hbs | 6 +- src/template/src/routers/demo.hbs | 1 - src/template/src/routers/user.hbs | 2 +- src/template/src/services/user.hbs | 14 +- src/template/src/utils/rand_utils.hbs | 3 +- src/template/templates/{404.html => 404.hbs} | 8 +- .../templates/{hello.html => hello.hbs} | 0 .../templates/{login.html => login.hbs} | 6 +- .../{user_list.html => user_list.hbs} | 22 +- ...user_list_page.html => user_list_page.hbs} | 0 src/utils/create_project.rs | 68 ++++-- 12 files changed, 279 insertions(+), 50 deletions(-) rename src/template/templates/{404.html => 404.hbs} (84%) rename src/template/templates/{hello.html => hello.hbs} (100%) rename src/template/templates/{login.html => login.hbs} (96%) rename src/template/templates/{user_list.html => user_list.hbs} (85%) rename src/template/templates/{user_list_page.html => user_list_page.hbs} (100%) diff --git a/locales/code_comment.yml b/locales/code_comment.yml index 97e0ddd..104d0db 100644 --- a/locales/code_comment.yml +++ b/locales/code_comment.yml @@ -201,3 +201,202 @@ login: th: เข้าสู่ระบบ el: Σύνδεση da: Log ind +user_list: + en: User list + zh_CN: 用户列表 + zh_TW: 使用者列表 + fr: Liste d'utilisateurs + ja: ユーザーリスト + es: Lista de usuarios + de: Benutzerliste + ru: Список пользователей + it: Lista utenti + pt: Lista de usuários + ko: 사용자 목록 + no: Brukerliste + is: Notendalisti + uk: Список користувачів + th: รายชื่อผู้ใช้ + el: Λίστα χρηστών + da: Brugerliste + +delete: + en: Delete + zh_CN: 删除 + zh_TW: 刪除 + fr: Supprimer + ja: 削除 + es: Eliminar + de: Löschen + ru: Удалить + it: Elimina + pt: Deletar + ko: 삭제 + no: Slett + is: Eyða + uk: Видалити + th: ลบ + el: Διαγραφή + da: Slet +return_to_homepage: + en: Return to homepage + zh_CN: 返回主页 + zh_TW: 返回主頁 + fr: Retour à la page d'accueil + ja: ホームページに戻る + es: Volver a la página principal + de: Zur Startseite zurückkehren + ru: Вернуться на главную страницу + it: Torna alla homepage + pt: Retornar à página inicial + ko: 홈페이지로 돌아가기 + no: Returner til hovedsiden + is: Fara aftur á heimasíðu + uk: Повернутися на головну сторінку + th: กลับสู่หน้าหลัก + el: Επιστροφή στην αρχική σελίδα + da: Returner til hjemmesiden +username: + en: Username + zh_CN: 用户名 + zh_TW: 用戶名 + fr: Nom d'utilisateur + ja: ユーザーネーム + es: Nombre de usuario + de: Benutzername + ru: Имя пользователя + it: Nome utente + pt: Nome de usuário + ko: 사용자 이름 + no: Brukernavn + is: Notendanafn + uk: Ім'я користувача + 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: 联系支持 + zh_TW: 聯繫支援 + fr: Contacter le support + ja: サポートに連絡する + es: Contactar con soporte + de: Support kontaktieren + ru: Связаться со службой поддержки + it: Contatta il supporto + pt: Contato com o suporte + ko: 지원에 문의하세요 + no: Kontaktstøtte + is: Hafa samband við þjónustu + uk: Звернутися до служби підтримки + th: ติดต่อฝ่ายสนับสนุน + el: Επικοινωνία με την υποστήριξη + da: Kontakt support +page_not_found: + en: Page Not Found + zh_CN: 页面未找到 + zh_TW: 找不到網頁 + fr: Page non trouvée + ja: ページが見つかりません + es: Página no encontrada + de: Seite nicht gefunden + ru: Страница не найдена + it: Pagina non trovata + pt: Página não encontrada + ko: 페이지를 찾을 수 없습니다 + no: Siden ble ikke funnet + is: Síða fannst ekki + uk: Сторінку не знайдено + th: ไม่พบหน้านี้ + el: Η σελίδα δεν βρέθηκε + da: Side ikke fundet +are_you_sure_you_want_to_delete: + en: Are you sure you want to delete? + zh_CN: 确定删除吗? + zh_TW: 確定要刪除嗎? + fr: Êtes-vous sûr de vouloir supprimer ? + ja: 削除してもよろしいですか? + es: ¿Estás seguro de que quieres eliminar? + de: Sind Sie sicher, dass Sie löschen möchten? + ru: Вы уверены, что хотите удалить? + it: Sei sicuro di voler eliminare? + pt: Tem certeza de que deseja excluir? + ko: 정말로 삭제하시겠습니까? + no: Er du sikker på at du vil slette? + is: Ertu viss um að þú viljir eyða? + uk: Ви впевнені, що хочете видалити? + th: คุณแน่ใจหรือว่าต้องการลบ? + el: Είστε σίγουροι ότι θέλετε να διαγράψετε; + da: Er du sikker på, at du vil slette? +yes: + en: Yes + zh_CN: 是 + zh_TW: 是 + fr: Oui + ja: はい + es: Sí + de: Ja + ru: Да + it: Sì + pt: Sim + ko: 예 + no: Ja + is: Já + uk: Так + th: ใช่ + el: Ναι + da: Ja +cancel: + en: Cancel + zh_CN: 取消 + zh_TW: 取消 + fr: Annuler + ja: キャンセル + es: Cancelar + de: Abbrechen + ru: Отмена + it: Annulla + pt: Cancelar + ko: 취소 + no: Avbryt + is: Hætta við + uk: Відмінити + th: ยกเลิก + el: Ακύρωση + da: Annuller +operation: + en: Operation + zh_CN: 操作 + zh_TW: 操作 + fr: Opération + ja: 操作 + es: Operación + de: Betrieb + ru: Операция + it: Operazione + pt: Operação + ko: 작업 + no: Operasjon + is: Aðgerð + uk: Операція + th: การดำเนินการ + el: Λειτουργία + da: Operation \ No newline at end of file diff --git a/src/template/src/db.hbs b/src/template/src/db.hbs index c8de91c..0d8864d 100644 --- a/src/template/src/db.hbs +++ b/src/template/src/db.hbs @@ -29,17 +29,17 @@ pub async fn init_db_conn() { {{#if is_sqlite}} SqlitePool::connect(&CFG.database.database_url) .await - .expect("数据库打开失败") + .expect("{{database_connection_failed}}") {{/if}} {{#if is_postgres}} PgPool::connect(&CFG.database.database_url) .await - .expect("数据库打开失败") + .expect("{{database_connection_failed}}") {{/if}} {{#if is_mysql}} MySqlPool::connect(&CFG.database.database_url) .await - .expect("数据库打开失败") + .expect("{{database_connection_failed}}") {{/if}} {{/if}} }) diff --git a/src/template/src/routers/demo.hbs b/src/template/src/routers/demo.hbs index f254021..794d583 100644 --- a/src/template/src/routers/demo.hbs +++ b/src/template/src/routers/demo.hbs @@ -27,7 +27,6 @@ pub async fn hello() -> AppResult<&'static str> { } {{/if}} mod tests { - use salvo::prelude::*; use salvo::test::{ResponseExt, TestClient}; use crate::config::CFG; diff --git a/src/template/src/routers/user.hbs b/src/template/src/routers/user.hbs index 3930e69..5b74028 100644 --- a/src/template/src/routers/user.hbs +++ b/src/template/src/routers/user.hbs @@ -6,7 +6,7 @@ use salvo::{ http::cookie::Cookie, oapi::extract::{FormBody, JsonBody}, writing::{Redirect, Text}, - Request, Response, + Response, }; use crate::{ app_error::AppResult, diff --git a/src/template/src/services/user.hbs b/src/template/src/services/user.hbs index 004ad1c..ea1cf2d 100644 --- a/src/template/src/services/user.hbs +++ b/src/template/src/services/user.hbs @@ -12,7 +12,7 @@ use crate::{ use uuid::Uuid; {{#if is_sqlx}} pub async fn add_user(req: UserAddRequest) -> AppResult { - let db = DB.get().ok_or(anyhow::anyhow!("数据库连接失败"))?; + let db = DB.get().ok_or(anyhow::anyhow!("{{database_connection_failed}}"))?; let id = Uuid::new_v4().to_string(); let hash_password = rand_utils::hash_password(req.password).await?; let _ = sqlx::query!( @@ -34,7 +34,7 @@ pub async fn add_user(req: UserAddRequest) -> AppResult { } pub async fn login(req: UserLoginRequest) -> AppResult { - let db = DB.get().ok_or(anyhow::anyhow!("数据库连接失败"))?; + let db = DB.get().ok_or(anyhow::anyhow!("{{database_connection_failed}}"))?; let user = sqlx::query_as!( User, r#" @@ -46,14 +46,14 @@ pub async fn login(req: UserLoginRequest) -> AppResult { .fetch_optional(db) .await?; if user.is_none() { - return Err(anyhow::anyhow!("用户不存在").into()); + return Err(anyhow::anyhow!("{{user_does_not_exist}}").into()); } let user = user.unwrap(); if rand_utils::verify_password(req.password, user.password) .await .is_err() { - return Err(anyhow::anyhow!("密码不正确").into()); + return Err(anyhow::anyhow!("{{incorrect_password}}").into()); } let (token, exp) = get_token(user.username.clone(), user.id.clone())?; let res = UserLoginResponse { @@ -66,7 +66,7 @@ pub async fn login(req: UserLoginRequest) -> AppResult { } pub async fn update_user(req: UserUpdateRequest) -> AppResult { - let db = DB.get().ok_or(anyhow::anyhow!("数据库连接失败"))?; + let db = DB.get().ok_or(anyhow::anyhow!("{{database_connection_failed}}"))?; let hash_password = rand_utils::hash_password(req.password).await?; let _ = sqlx::query!( r#" @@ -88,7 +88,7 @@ pub async fn update_user(req: UserUpdateRequest) -> AppResult { } pub async fn delete_user(req: UserDeleteRequest) -> AppResult<()> { - let db = DB.get().ok_or(anyhow::anyhow!("数据库连接失败"))?; + let db = DB.get().ok_or(anyhow::anyhow!("{{database_connection_failed}}"))?; sqlx::query!( r#" DELETE FROM users @@ -103,7 +103,7 @@ pub async fn delete_user(req: UserDeleteRequest) -> AppResult<()> { } pub async fn users() -> AppResult> { - let db = DB.get().ok_or(anyhow::anyhow!("数据库连接失败"))?; + let db = DB.get().ok_or(anyhow::anyhow!("{{database_connection_failed}}"))?; let users = sqlx::query_as!( User, r#" diff --git a/src/template/src/utils/rand_utils.hbs b/src/template/src/utils/rand_utils.hbs index ed05e3e..5668317 100644 --- a/src/template/src/utils/rand_utils.hbs +++ b/src/template/src/utils/rand_utils.hbs @@ -2,7 +2,8 @@ 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 { iter::repeat(()) diff --git a/src/template/templates/404.html b/src/template/templates/404.hbs similarity index 84% rename from src/template/templates/404.html rename to src/template/templates/404.hbs index e52aac0..dd97fe6 100644 --- a/src/template/templates/404.html +++ b/src/template/templates/404.hbs @@ -3,7 +3,7 @@ - 404 Page Not Found + 404 {{page_not_found}} - Page not found + {{page_not_found}}
Go back home{{return_to_homepage}} Contact support {{contact_support}}
diff --git a/src/template/templates/hello.html b/src/template/templates/hello.hbs similarity index 100% rename from src/template/templates/hello.html rename to src/template/templates/hello.hbs diff --git a/src/template/templates/login.html b/src/template/templates/login.hbs similarity index 96% rename from src/template/templates/login.html rename to src/template/templates/login.hbs index 502560f..53133a3 100644 --- a/src/template/templates/login.html +++ b/src/template/templates/login.hbs @@ -15,14 +15,14 @@

- login + {{login}}

- +
- +

- 用户列表 + {{user_list}}

-

名称

@@ -26,11 +25,11 @@

scope="col" class="py-3.5 pl-4 pr-3 text-left text-sm font-semibold text-gray-900 sm:pl-0" > - Name + {{username}} - Delete - Delete + {{delete}} + {{operation}} @@ -40,20 +39,21 @@

- {{user.id}} + [[user.id]] - {{user.username}} + [[user.username]] 'Content-Type': 'application/json' }, body: JSON.stringify({ - id: '{{user.id}}', + id: '[[user.id]]', }) }) .then(response => { @@ -81,7 +81,7 @@

}) " >删除删除{{delete}}{{delete}} diff --git a/src/template/templates/user_list_page.html b/src/template/templates/user_list_page.hbs similarity index 100% rename from src/template/templates/user_list_page.html rename to src/template/templates/user_list_page.hbs diff --git a/src/utils/create_project.rs b/src/utils/create_project.rs index 01e9670..1284d14 100644 --- a/src/utils/create_project.rs +++ b/src/utils/create_project.rs @@ -89,6 +89,20 @@ fn write_project_file( "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"), + "login":t!("login"), + "user_list":t!("user_list"), + "are_you_sure_you_want_to_delete":t!("are_you_sure_you_want_to_delete"), + "page_not_found":t!("page_not_found"), + "contact_support":t!("contact_support"), + "return_to_homepage":t!("return_to_homepage"), + "delete":t!("delete"), + "yes":t!("yes"), + "cancel":t!("cancel"), + "operation":t!("operation"), }); if is_sqlx { // Add sqlx dependencies @@ -125,7 +139,7 @@ fn write_project_file( "version": "0.5.2", }); data["dependencies"] = dependencies; - } + } std::fs::create_dir_all(project_path)?; @@ -207,28 +221,44 @@ fn write_project_file( //templates let template_path = project_path.join("templates"); std::fs::create_dir_all(&template_path)?; + //template/hello.html - let hello_html_template = include_bytes!("../template/templates/hello.html"); - let mut hello_html_file = File::create(template_path.join("hello.html"))?; - hello_html_file.write_all(hello_html_template)?; + let hello_template = include_str!("../template/templates/hello.hbs"); + let mut hello_file = File::create(template_path.join("hello.html"))?; + hello_file.write_all(hello_template.as_bytes())?; + //template/handle_404.html - let handle_404_template = include_bytes!("../template/templates/404.html"); + let handle_404_template = include_str!("../template/templates/404.hbs"); + let handle_404_template_rendered = + handlebars.render_template(handle_404_template, &data)?; let mut handle_404_file = File::create(template_path.join("handle_404.html"))?; - handle_404_file.write_all(handle_404_template)?; + handle_404_file.write_all(handle_404_template_rendered.as_bytes())?; + if need_db_conn { //template/login.html - let login_html_template = include_bytes!("../template/templates/login.html"); - let mut login_html_file = File::create(template_path.join("login.html"))?; - login_html_file.write_all(login_html_template)?; - //template/user_list_page - let user_list_page_template = - include_bytes!("../template/templates/user_list_page.html"); - let mut user_list_page_file = File::create(template_path.join("user_list_page.html"))?; - user_list_page_file.write_all(user_list_page_template)?; + let login_template = include_str!("../template/templates/login.hbs"); + let login_template_rendered = handlebars.render_template(login_template, &data)?; + let mut login_file = File::create(template_path.join("login.html"))?; + login_file.write_all(login_template_rendered.as_bytes())?; + //template/user_list.html - let user_list_html_template = include_bytes!("../template/templates/user_list.html"); - let mut user_list_html_file = File::create(template_path.join("user_list.html"))?; - user_list_html_file.write_all(user_list_html_template)?; + let user_list_template = include_str!("../template/templates/user_list.hbs"); + let user_list_template_rendered = + handlebars.render_template(user_list_template, &data)?; + let mut user_list_file = File::create(template_path.join("user_list.html"))?; + user_list_file.write_all( + user_list_template_rendered + .replace("[[", "{{") + .replace("]]", "}}") + .as_bytes(), + )?; + + //template/user_list_page.html + let user_list_page_template = include_str!("../template/templates/user_list_page.hbs"); + let user_list_page_template_rendered = + handlebars.render_template(user_list_page_template, &data)?; + let mut user_list_page_file = File::create(template_path.join("user_list_page.html"))?; + user_list_page_file.write_all(user_list_page_template_rendered.as_bytes())?; } } //src/router @@ -317,9 +347,9 @@ fn write_project_file( let data_path = project_path.join("data"); std::fs::create_dir_all(&data_path)?; //data/demo.db - let demo_db_bytes= include_bytes!("../template/data/demo.db"); + let demo_db_bytes = include_bytes!("../template/data/demo.db"); let mut demo_db_file = File::create(data_path.join("demo.db"))?; - demo_db_file.write_all(demo_db_bytes)?; + demo_db_file.write_all(demo_db_bytes)?; } //migrations let migrations_path = project_path.join("migrations"); From fc387c06050f34a478917a75a1420b36b02465f6 Mon Sep 17 00:00:00 2001 From: Fankai Liu Date: Sun, 8 Oct 2023 22:59:48 +0800 Subject: [PATCH 10/19] Eliminate post-generation warnings --- src/template/src/routers/demo.hbs | 2 ++ src/template/src/routers/mod.hbs | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/template/src/routers/demo.hbs b/src/template/src/routers/demo.hbs index 794d583..9942a22 100644 --- a/src/template/src/routers/demo.hbs +++ b/src/template/src/routers/demo.hbs @@ -26,6 +26,8 @@ pub async fn hello() -> AppResult<&'static str> { Ok("Hello World from salvo") } {{/if}} + +#[allow(unused_imports)] mod tests { use salvo::test::{ResponseExt, TestClient}; diff --git a/src/template/src/routers/mod.hbs b/src/template/src/routers/mod.hbs index 54a5bda..569040c 100644 --- a/src/template/src/routers/mod.hbs +++ b/src/template/src/routers/mod.hbs @@ -9,7 +9,7 @@ use self::{ {{#if need_db_conn}} {{#if is_web_site}} user::{ - delete_user, get_users, login_page, post_add_user, post_login, post_update_user, + delete_user,login_page, post_add_user, post_login, post_update_user, user_list_page, }, {{else}} From 19ad60e395aafdf413a2ea9382638271df8c4a6e Mon Sep 17 00:00:00 2001 From: Fankai Liu Date: Mon, 9 Oct 2023 23:08:53 +0800 Subject: [PATCH 11/19] Preparing for a new release --- locales/code_comment.yml | 51 ++++++++++++++++++++++++++++- src/template/.env.hbs | 2 +- src/template/config/config.hbs | 2 +- src/template/data/demo.db | Bin 24576 -> 24576 bytes src/template/data/demo.db-shm | Bin 32768 -> 0 bytes src/template/data/demo.db-wal | 0 src/template/data/init_sql.sql | 3 ++ src/template/src/app_error.hbs | 10 ++++++ src/template/src/main_template.hbs | 4 +++ src/template/src/middleware/jwt.rs | 1 + src/template/src/routers/demo.hbs | 7 ++-- src/template/src/routers/mod.hbs | 2 ++ src/template/src/routers/user.hbs | 15 +++++++++ src/utils/create_project.rs | 23 ++++++++++--- src/utils/get_selection.rs | 14 ++++---- 15 files changed, 118 insertions(+), 16 deletions(-) delete mode 100644 src/template/data/demo.db-shm delete mode 100644 src/template/data/demo.db-wal create mode 100644 src/template/data/init_sql.sql diff --git a/locales/code_comment.yml b/locales/code_comment.yml index 104d0db..7e9d7a2 100644 --- a/locales/code_comment.yml +++ b/locales/code_comment.yml @@ -399,4 +399,53 @@ operation: uk: Операція th: การดำเนินการ el: Λειτουργία - da: Operation \ No newline at end of file + da: Operation +create_success_sqlx: + en: 🎯 You have chosen sqlx, documentation can be viewed here:https://github.com/launchbadge/sqlx + zh_CN: 🎯 您选择了sqlx,文档可以在这里查看:https://github.com/launchbadge/sqlx + zh_TW: 🎯 您選擇了sqlx,文檔可以在這裡查看:https://github.com/launchbadge/sqlx + fr: 🎯 Vous avez choisi sqlx, la documentation peut être consultée ici:https://github.com/launchbadge/sqlx + ja: 🎯 sqlxを選択しました。ドキュメントはこちらからご覧ください:https://github.com/launchbadge/sqlx + es: 🎯 Ha elegido sqlx, la documentación se puede ver aquí:https://github.com/launchbadge/sqlx + de: 🎯 Sie haben sqlx gewählt, die Dokumentation kann hier eingesehen werden:https://github.com/launchbadge/sqlx + ru: 🎯 Вы выбрали sqlx, документацию можно посмотреть здесь:https://github.com/launchbadge/sqlx + it: 🎯 Hai scelto sqlx, la documentazione può essere visualizzata qui:https://github.com/launchbadge/sqlx + pt: 🎯 Você escolheu sqlx, a documentação pode ser visualizada aqui:https://github.com/launchbadge/sqlx + ko: 🎯 sqlx를 선택했습니다. 여기에서 문서를 볼 수 있습니다:https://github.com/launchbadge/sqlx + no: 🎯 Du har valgt sqlx, dokumentasjonen kan ses her:https://github.com/launchbadge/sqlx + is: 🎯 Þú hefur valið sqlx, hægt er að skoða kennslu hér:https://github.com/launchbadge/sqlx + uk: 🎯 Ви вибрали sqlx, документацію можна переглянути тут:https://github.com/launchbadge/sqlx + th: 🎯 คุณได้เลือก sqlx คุณสามารถดูเอกสารได้ที่นี่:https://github.com/launchbadge/sqlx + el: 🎯 Επιλέξατε sqlx, η τεκμηρίωση μπορεί να προβληθεί εδώ:https://github.com/launchbadge/sqlx + da: 🎯 Du har valgt sqlx, dokumentationen kan ses her:https://github.com/launchbadge/sqlx +create_success_sqlx_sqlite: + en: 🎯 Default database created in `/data/demo.db`.\n After running it, you can access /login with the default username:'zhangsan' and password:'123'. + zh_CN: 🎯 默认数据库创建在`/data/demo.db`。\n 运行后,您可以使用默认用户名:'zhangsan'和密码:'123'访问/login。 + zh_TW: 🎯 預設資料庫創建在`/data/demo.db`。\n 運行後,您可以使用預設用戶名:'zhangsan'和密碼:'123'訪問/login。 + fr: 🎯 Base de données par défaut créée dans `/data/demo.db`. \n Après l'avoir exécuté, vous pouvez accéder à /login avec le nom d'utilisateur par défaut:'zhangsan' et le mot de passe:'123'. + ja: 🎯 デフォルトのデータベースが`/data/demo.db`に作成されました。\n 実行後、デフォルトのユーザー名:'zhangsan'とパスワード:'123'で/loginにアクセスできます。 + es: 🎯 Base de datos predeterminada creada en `/data/demo.db`. \n Después de ejecutarlo, puede acceder a /login con el nombre de usuario predeterminado:'zhangsan' y la contraseña:'123'. + de: 🎯 Standarddatenbank erstellt in `/data/demo.db`. \n Nach dem Ausführen können Sie mit dem Standardbenutzernamen:'zhangsan' und dem Passwort:'123' auf /login zugreifen. + ru: 🎯 База данных по умолчанию создана в `/data/demo.db`. \n После запуска вы можете получить доступ к /login с именем пользователя по умолчанию:'zhangsan' и паролем:'123'. + it: 🎯 Database predefinita creata in `/data/demo.db`. \n Dopo l'esecuzione, è possibile accedere a /login con l'username predefinito:'zhangsan' e la password:'123'. + pt: 🎯 Banco de dados padrão criado em `/data/demo.db`. \n Após executá-lo, você pode acessar /login com o nome de usuário padrão:'zhangsan' e a senha:'123'. + ko: 🎯 기본 데이터베이스가 `/data/demo.db`에 생성되었습니다. \n 실행 후 기본 사용자 이름:'zhangsan'과 비밀번호:'123'으로 /login에 접속할 수 있습니다. + no: 🎯 Standarddatabasen er opprettet i `/data/demo.db`. \n Etter å ha kjørt det, kan du få tilgang til /login med standard brukernavn:'zhangsan' og passord:'123'. + is: 🎯 Sjálfgefin gagnagrunn búin til í `/data/demo.db`. \n Eftir að hafa keyrt það, getur þú fengið aðgang að /login með sjálfgefnum notandanafni:'zhangsan' og lykilorði:'123'. + uk: 🎯 База даних за замовчуванням створена в `/data/demo.db`. \n Після запуску ви можете отримати доступ до /login за допомогою імені користувача за замовчуванням:'zhangsan' та пароля:'123'. + th: 🎯 สร้างฐานข้อมูลเริ่มต้นใน `/data/demo.db`. \n หลังจากเรียกใช้งาน คุณสามารถเข้าถึง /login ด้วยชื่อผู้ใช้เริ่มต้น:'zhangsan' และรหัสผ่าน:'123'. + el: 🎯 Δημιουργήθηκε η προεπιλεγμένη βάση δεδομένων στο `/data/demo.db`. \n Μετά την εκτέλεσή του, μπορείτε να αποκτήσετε πρόσβαση στο /login με το όνομα χρήστη:'zhangsan' και τον κωδικό πρόσβασης:'123'. + da: 🎯 Standarddatabasen er oprettet i `/data/demo.db`. \n Efter at have kørt det, kan du få adgang til /login med standardbrugernavnet:'zhangsan' og adgangskoden:'123'. +create_success_mysql_or_pgsql: + en: Modify the database connection string in .env and config/config.toml\n Run data/init.sql to add a default string.\n After running, you can access /login with default username:'zhangsan' and password:'123'. + zh_CN: 修改.env和config/config.toml中的数据库连接字符串\n 运行data/init.sql添加默认字符串。\n 运行后,您可以使用默认用户名:'zhangsan'和密码:'123'访问/login。 + zh_TW: 修改.env和config/config.toml中的資料庫連接字串\n 運行data/init.sql添加預設字串。\n 運行後,您可以使用預設用戶名:'zhangsan'和密碼:'123'訪問/login。 + fr: Modifiez la chaîne de connexion à la base de données dans .env et config/config.toml\n Exécutez data/init.sql pour ajouter une chaîne par défaut.\n Après l'avoir exécuté, vous pouvez accéder à /login avec le nom d'utilisateur par défaut:'zhangsan' et le mot de passe:'123'. + ja: .envとconfig/config.tomlのデータベース接続文字列を変更します\n data/init.sqlを実行してデフォルトの文字列を追加します。\n 実行後、デフォルトのユーザー名:'zhangsan'とパスワード:'123'で/loginにアクセスできます。 + es: Modifique la cadena de conexión a la base de datos en .env y config/config.toml\n Ejecute data/init.sql para agregar una cadena predeterminada.\n Después de ejecutarlo, puede acceder a /login con el nombre de usuario predeterminado:'zhangsan' y la contraseña:'123'. + de: Ändern Sie die Verbindungszeichenfolge zur Datenbank in .env und config/config.toml\n Führen Sie data/init.sql aus, um eine Standardzeichenfolge hinzuzufügen.\n Nach dem Ausführen können Sie mit dem Standardbenutzernamen:'zhangsan' und dem Passwort:'123' auf /login zugreifen. + ru: Измените строку подключения к базе данных в .env и config/config.toml\n Запустите data/init.sql, чтобы добавить строку по умолчанию.\n После запуска вы можете получить доступ к /login с именем пользователя по умолчанию:'zhangsan' и паролем:'123'. + it: Modifica la stringa di connessione al database in .env e config/config.toml\n Esegui data/init.sql per aggiungere una stringa predefinita.\n Dopo l'esecuzione, è possibile accedere a /login con l'username predefinito:'zhangsan' e la password:'123'. + pt: Modifique a string de conexão do banco de dados em .env e config/config.toml\n Execute data/init.sql para adicionar uma string padrão.\n Após executá-lo, você pode acessar /login com o nome de usuário padrão:'zhangsan' e a senha:'123'. + + diff --git a/src/template/.env.hbs b/src/template/.env.hbs index ffe8cd2..78fd725 100644 --- a/src/template/.env.hbs +++ b/src/template/.env.hbs @@ -1,7 +1,7 @@ {{#if is_postgres}} DATABASE_URL=postgresql://liufankai:1@localhost/salvo_demo {{/if}} -{{#if is_sqlx}} +{{#if is_sqlite}} DATABASE_URL="sqlite:data/demo.db" {{/if}} {{#if is_mysql}} diff --git a/src/template/config/config.hbs b/src/template/config/config.hbs index 2ef4fdf..fe495b9 100644 --- a/src/template/config/config.hbs +++ b/src/template/config/config.hbs @@ -11,7 +11,7 @@ database_url= "sqlite:data/demo.db" database_url="postgresql://liufankai:1@localhost/salvo_demo" {{/if}} {{#if is_mysql}} -database_url="postgresql://liufankai:1@localhost/salvo_demo" +database_url="mysql://root:981109@localhost/salvo_demo" {{/if}} {{/if}} [jwt] diff --git a/src/template/data/demo.db b/src/template/data/demo.db index e983043eec2cd2ab0bad65f2036bbe85564a1055..9e3f7f1d4d4d20351f179f6c3178c7dc31e4884c 100644 GIT binary patch delta 48 zcmZoTz}RqrQ6@OhC$l6~AuYcsH?c&)m_dMnk&(ecL4kpRfq9~gGb=NLo`3DelsWML DON 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/demo.hbs b/src/template/src/routers/demo.hbs index 9942a22..dfdaf06 100644 --- a/src/template/src/routers/demo.hbs +++ b/src/template/src/routers/demo.hbs @@ -1,8 +1,11 @@ {{#if is_web_site}} use askama::Template; {{/if}} +{{#if is_web_site}} use salvo::{endpoint, writing::Text, Request, Response}; - +{{else}} +use salvo::endpoint; +{{/if}} use crate::app_error::AppResult; {{#if is_web_site}} @@ -30,7 +33,7 @@ pub async fn hello() -> AppResult<&'static str> { #[allow(unused_imports)] mod tests { use salvo::test::{ResponseExt, TestClient}; - + use salvo::Service; use crate::config::CFG; #[tokio::test] diff --git a/src/template/src/routers/mod.hbs b/src/template/src/routers/mod.hbs index 569040c..04ae14c 100644 --- a/src/template/src/routers/mod.hbs +++ b/src/template/src/routers/mod.hbs @@ -1,4 +1,6 @@ +{{#if need_db_conn}} use crate::middleware::jwt::jwt_hoop; +{{/if}} use salvo::{ prelude::{CatchPanic, Logger, OpenApi, SwaggerUi}, Router, diff --git a/src/template/src/routers/user.hbs b/src/template/src/routers/user.hbs index 5b74028..8ad1089 100644 --- a/src/template/src/routers/user.hbs +++ b/src/template/src/routers/user.hbs @@ -1,6 +1,7 @@ {{#if is_web_site}} use askama::Template; {{/if}} +{{#if is_web_site}} use salvo::{ endpoint, http::cookie::Cookie, @@ -16,6 +17,20 @@ use crate::{ }, 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")] diff --git a/src/utils/create_project.rs b/src/utils/create_project.rs index 1284d14..3ea5325 100644 --- a/src/utils/create_project.rs +++ b/src/utils/create_project.rs @@ -33,11 +33,20 @@ pub fn create_project(project: Project) -> Result<()> { let config = get_user_selected()?; match config { Some(config) => { - write_project_file(project_path, config, project.clone())?; + write_project_file(project_path, config.clone(), project.clone())?; init_git(project_path)?; success(t!("create_success", project_name = project_name).replace(r"\n", "\n")); + if config.db_conn_type== DbConnectionType::Sqlx { + success(t!("create_success_sqlx", project_name = project_name).replace(r"\n", "\n")); + if config.db_type == DbType::Sqlite { + success(t!("create_success_sqlx_sqlite").replace(r"\n", "\n")); + } + else { + success(t!("create_success_mysql_or_pgsql").replace(r"\n", "\n")); + } + } } None => anyhow::bail!("cli quit!"), } @@ -342,15 +351,21 @@ fn write_project_file( models_user_file.write_all(models_user_rendered.as_bytes())?; if is_sqlx { + //data + let data_path = project_path.join("data"); + std::fs::create_dir_all(&data_path)?; if is_sqlite { - //data - let data_path = project_path.join("data"); - std::fs::create_dir_all(&data_path)?; //data/demo.db let demo_db_bytes = include_bytes!("../template/data/demo.db"); let mut demo_db_file = File::create(data_path.join("demo.db"))?; demo_db_file.write_all(demo_db_bytes)?; } + else{ + //data/init_sql.sql + let init_sql_bytes = include_bytes!("../template/data/init_sql.sql"); + let mut init_sql_file = File::create(data_path.join("init_sql.sql"))?; + init_sql_file.write_all(init_sql_bytes)?; + } //migrations let migrations_path = project_path.join("migrations"); std::fs::create_dir_all(&migrations_path)?; diff --git a/src/utils/get_selection.rs b/src/utils/get_selection.rs index 826c018..81e81d6 100644 --- a/src/utils/get_selection.rs +++ b/src/utils/get_selection.rs @@ -2,7 +2,7 @@ use anyhow::Result; use dialoguer::{console::Style, theme::ColorfulTheme, Select}; use rust_i18n::t; -#[derive(Debug)] +#[derive(Debug,Clone)] pub struct UserSelected { pub template_type: TemplateType, pub db_type: DbType, @@ -34,9 +34,9 @@ pub fn get_user_selected() -> Result> { }; let db_conn_types = &[ t!("db_conn_types_sqlx"), - t!("db_conn_types_diesel"), - t!("db_conn_types_sea_orm"), - t!("db_conn_types_rbatis"), + // t!("db_conn_types_diesel"), + // t!("db_conn_types_sea_orm"), + // t!("db_conn_types_rbatis"), t!("db_conn_types_nothing"), // "custom", ]; @@ -83,20 +83,20 @@ pub fn get_user_selected() -> Result> { db_conn_type, })) } -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq,Clone)] pub enum TemplateType { SalvoWebSite, SalvoWebApi, } -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq,Clone)] pub enum DbType { Sqlite, Mysql, Postgres, } -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq,Clone)] pub enum DbConnectionType { Sqlx, Diesel, From 1d5f6185248235ab60d4843cbedbab7c8d7c99fa Mon Sep 17 00:00:00 2001 From: Fankai Liu Date: Tue, 10 Oct 2023 20:54:06 +0800 Subject: [PATCH 12/19] add sqlx support --- locales/code_comment.yml | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/locales/code_comment.yml b/locales/code_comment.yml index 7e9d7a2..a5f48df 100644 --- a/locales/code_comment.yml +++ b/locales/code_comment.yml @@ -437,15 +437,20 @@ create_success_sqlx_sqlite: el: 🎯 Δημιουργήθηκε η προεπιλεγμένη βάση δεδομένων στο `/data/demo.db`. \n Μετά την εκτέλεσή του, μπορείτε να αποκτήσετε πρόσβαση στο /login με το όνομα χρήστη:'zhangsan' και τον κωδικό πρόσβασης:'123'. da: 🎯 Standarddatabasen er oprettet i `/data/demo.db`. \n Efter at have kørt det, kan du få adgang til /login med standardbrugernavnet:'zhangsan' og adgangskoden:'123'. create_success_mysql_or_pgsql: - en: Modify the database connection string in .env and config/config.toml\n Run data/init.sql to add a default string.\n After running, you can access /login with default username:'zhangsan' and password:'123'. - zh_CN: 修改.env和config/config.toml中的数据库连接字符串\n 运行data/init.sql添加默认字符串。\n 运行后,您可以使用默认用户名:'zhangsan'和密码:'123'访问/login。 - zh_TW: 修改.env和config/config.toml中的資料庫連接字串\n 運行data/init.sql添加預設字串。\n 運行後,您可以使用預設用戶名:'zhangsan'和密碼:'123'訪問/login。 - fr: Modifiez la chaîne de connexion à la base de données dans .env et config/config.toml\n Exécutez data/init.sql pour ajouter une chaîne par défaut.\n Après l'avoir exécuté, vous pouvez accéder à /login avec le nom d'utilisateur par défaut:'zhangsan' et le mot de passe:'123'. - ja: .envとconfig/config.tomlのデータベース接続文字列を変更します\n data/init.sqlを実行してデフォルトの文字列を追加します。\n 実行後、デフォルトのユーザー名:'zhangsan'とパスワード:'123'で/loginにアクセスできます。 - es: Modifique la cadena de conexión a la base de datos en .env y config/config.toml\n Ejecute data/init.sql para agregar una cadena predeterminada.\n Después de ejecutarlo, puede acceder a /login con el nombre de usuario predeterminado:'zhangsan' y la contraseña:'123'. - de: Ändern Sie die Verbindungszeichenfolge zur Datenbank in .env und config/config.toml\n Führen Sie data/init.sql aus, um eine Standardzeichenfolge hinzuzufügen.\n Nach dem Ausführen können Sie mit dem Standardbenutzernamen:'zhangsan' und dem Passwort:'123' auf /login zugreifen. - ru: Измените строку подключения к базе данных в .env и config/config.toml\n Запустите data/init.sql, чтобы добавить строку по умолчанию.\n После запуска вы можете получить доступ к /login с именем пользователя по умолчанию:'zhangsan' и паролем:'123'. - it: Modifica la stringa di connessione al database in .env e config/config.toml\n Esegui data/init.sql per aggiungere una stringa predefinita.\n Dopo l'esecuzione, è possibile accedere a /login con l'username predefinito:'zhangsan' e la password:'123'. - pt: Modifique a string de conexão do banco de dados em .env e config/config.toml\n Execute data/init.sql para adicionar uma string padrão.\n Após executá-lo, você pode acessar /login com o nome de usuário padrão:'zhangsan' e a senha:'123'. - - + en: Modify the database connection string in .env and config/config.toml\n Run this SQL in the database to add default data.\n After running, you can access /login with default username:'zhangsan' and password:'123'. + zh_CN: 修改.env和config/config.toml中的数据库连接字符串\n 在数据库中运行此SQL以添加默认数据。\n 运行后,您可以使用默认用户名:'zhangsan'和密码:'123'访问/login。 + zh_TW: 修改.env和config/config.toml中的資料庫連接字串\n 在資料庫中運行此SQL以添加預設資料。\n 運行後,您可以使用預設用戶名:'zhangsan'和密碼:'123'訪問/login。 + fr: Modifiez la chaîne de connexion à la base de données dans .env et config/config.toml\n Exécutez ce SQL dans la base de données pour ajouter des données par défaut.\n Après l'exécution, vous pouvez accéder à /login avec le nom d'utilisateur par défaut:'zhangsan' et le mot de passe:'123'. + ja: .envとconfig/config.tomlのデータベース接続文字列を変更します\n データベースでこのSQLを実行してデフォルトデータを追加します。\n 実行後、デフォルトのユーザー名:'zhangsan'とパスワード:'123'で/loginにアクセスできます。 + es: Modifique la cadena de conexión de la base de datos en .env y config/config.toml\n Ejecute este SQL en la base de datos para agregar datos predeterminados.\n Después de ejecutarlo, puede acceder a /login con el nombre de usuario predeterminado:'zhangsan' y la contraseña:'123'. + de: Ändern Sie die Verbindungszeichenfolge zur Datenbank in .env und config/config.toml\n Führen Sie diesen SQL in der Datenbank aus, um Standarddaten hinzuzufügen.\n Nach dem Ausführen können Sie mit dem Standardbenutzernamen:'zhangsan' und dem Passwort:'123' auf /login zugreifen. + ru: Измените строку подключения к базе данных в .env и config/config.toml\n Запустите этот SQL в базе данных, чтобы добавить данные по умолчанию.\n После запуска вы можете получить доступ к /login с именем пользователя по умолчанию:'zhangsan' и паролем:'123'. + it: Modifica la stringa di connessione al database in .env e config/config.toml\n Esegui questo SQL nel database per aggiungere dati predefiniti.\n Dopo l'esecuzione, è possibile accedere a /login con l'username predefinito:'zhangsan' e la password:'123'. + pt: Modifique a string de conexão do banco de dados em .env e config/config.toml\n Execute este SQL no banco de dados para adicionar dados padrão.\n Após a execução, você pode acessar /login com o nome de usuário padrão:'zhangsan' e a senha:'123'. + ko: .env 및 config/config.toml에서 데이터베이스 연결 문자열을 수정하십시오.\n 데이터베이스에서이 SQL을 실행하여 기본 데이터를 추가하십시오.\n 실행 후 기본 사용자 이름:'zhangsan'과 비밀번호:'123'으로 /login에 액세스 할 수 있습니다. + no: Endre database-tilkoblingsstrengen i .env og config/config.toml\n Kjør denne SQL-en i databasen for å legge til standarddata.\n Etter å ha kjørt det, kan du få tilgang til /login med standard brukernavn:'zhangsan' og passord:'123'. + is: Breyttu tengingu strenginn í .env og config/config.toml\n Keyrið þetta SQL í gagnagrunninum til að bæta við sjálfgefnum gögnum.\n Eftir að hafa keyrt það, getur þú fengið aðgang að /login með sjálfgefnum notandanafni:'zhangsan' og lykilorði:'123'. + uk: Змініть рядок підключення до бази даних в .env та config/config.toml\n Виконайте цей SQL в базі даних, щоб додати дані за замовчуванням.\n Після запуску ви можете отримати доступ до /login за допомогою імені користувача за замовчуванням:'zhangsan' та пароля:'123'. + th: แก้ไขสตริงการเชื่อมต่อฐานข้อมูลใน .env และ config/config.toml\n รัน SQL นี้ในฐานข้อมูลเพื่อเพิ่มข้อมูลเริ่มต้น\n หลังจากเรียกใช้งาน คุณสามารถเข้าถึง /login ด้วยชื่อผู้ใช้เริ่มต้น:'zhangsan' และรหัสผ่าน:'123'. + el: Τροποποιήστε τη συμβολοσειρά σύνδεσης της βάσης δεδομένων στα .env και config/config.toml\n Εκτελέστε αυτή την SQL στη βάση δεδομένων για να προσθέσετε προεπιλεγμένα δεδομένα.\n Μετά την εκτέλεση, μπορείτε να έχετε πρόσβαση στο /login με προεπιλεγμένο όνομα χρήστη:'zhangsan' και κωδικό πρόσβασης:'123'. + da: Rediger databaseforbindelsesstrengen i .env og config/config.toml\n Kør denne SQL i databasen for at tilføje standarddata.\n Efter at have kørt det, kan du få adgang til /login med standardbrugernavnet:'zhangsan' og adgangskoden:'123'. \ No newline at end of file From 3ba7b7e8d0dd5feffa7babc3b13c86146a075bf5 Mon Sep 17 00:00:00 2001 From: Fankai Liu Date: Tue, 10 Oct 2023 21:14:28 +0800 Subject: [PATCH 13/19] change introduction --- Cargo.toml | 4 ++-- src/utils/create_project.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index a3450ee..f4efade 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,8 +3,8 @@ name = "salvo-cli" version = "0.1.5" edition = "2021" authors = ["Fankai Liu liufankai137@outlook.com"] -keywords = ["salvo", "cli"] -description = "This CLI tool aims to streamline the process of setting up a new project by generating a template structure. Key features might include generating boilerplate code, configuring necessary dependencies, and setting up a basic project structure suitable for Salvo-based applications" +keywords = ["salvo", "cli","web template"] +description = "This CLI tool is designed to streamline the creation of new Salvo web projects through the generation of template structures. It offers the flexibility to select from web API templates or web site templates, and the convenience of choosing a database connector. It auto-generates foundational code to give users a head start in their development process." license = "MIT/Apache-2.0" repository = "https://github.com/fankaiLiu/salvo-cli" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/src/utils/create_project.rs b/src/utils/create_project.rs index 3ea5325..b136ff0 100644 --- a/src/utils/create_project.rs +++ b/src/utils/create_project.rs @@ -361,7 +361,7 @@ fn write_project_file( demo_db_file.write_all(demo_db_bytes)?; } else{ - //data/init_sql.sql + //data/init_sql.sql let init_sql_bytes = include_bytes!("../template/data/init_sql.sql"); let mut init_sql_file = File::create(data_path.join("init_sql.sql"))?; init_sql_file.write_all(init_sql_bytes)?; From e1b1484a874add554699b5ad746cbd1136107244 Mon Sep 17 00:00:00 2001 From: Fankai Liu Date: Tue, 10 Oct 2023 21:19:15 +0800 Subject: [PATCH 14/19] release of new versions --- Cargo.lock | 2 +- Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e74ae87..17fdbac 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -798,7 +798,7 @@ checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" [[package]] name = "salvo-cli" -version = "0.1.5" +version = "0.1.6" dependencies = [ "ansi_term", "anyhow", diff --git a/Cargo.toml b/Cargo.toml index f4efade..4c04d42 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "salvo-cli" -version = "0.1.5" +version = "0.1.6" edition = "2021" authors = ["Fankai Liu liufankai137@outlook.com"] keywords = ["salvo", "cli","web template"] From d858a42cfbbac9cb9ab7e703392839700032d87b Mon Sep 17 00:00:00 2001 From: Fankai Liu Date: Tue, 10 Oct 2023 21:21:19 +0800 Subject: [PATCH 15/19] edit keywords --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 4c04d42..236caed 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ name = "salvo-cli" version = "0.1.6" edition = "2021" authors = ["Fankai Liu liufankai137@outlook.com"] -keywords = ["salvo", "cli","web template"] +keywords = ["salvo", "cli","template"] description = "This CLI tool is designed to streamline the creation of new Salvo web projects through the generation of template structures. It offers the flexibility to select from web API templates or web site templates, and the convenience of choosing a database connector. It auto-generates foundational code to give users a head start in their development process." license = "MIT/Apache-2.0" repository = "https://github.com/fankaiLiu/salvo-cli" From d80095480c33db4f8a4ab42a07e0645e50a93642 Mon Sep 17 00:00:00 2001 From: Fankai Liu Date: Tue, 10 Oct 2023 22:09:09 +0800 Subject: [PATCH 16/19] add introduction --- README.md | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index a20c87b..35eb877 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,10 @@ + # Salvo CLI -Salvo CLI is a command-line interface tool for the Salvo web framework. It helps streamline the process of setting up a new Salvo project by generating a template structure. +Salvo CLI is a command-line interface tool for the [Salvo](https://github.com/salvo-rs/salvo) web framework. It helps streamline the process of setting up a new Salvo project by generating a template structure. ## Installation -Before you can use the Salvo CLI, you need to install it. Assuming you have Rust and Cargo installed, you can install Salvo CLI with the following command: - ```bash cargo install salvo-cli ``` @@ -15,13 +14,24 @@ To create a new Salvo project, use the new command followed by the name of your ```bash salvo-cli new project_name ``` -This will create a new directory named project_name, and it will set up a basic project structure suitable for a Salvo-based application. - ## Update ```bashs cargo install --force salvo-cli ``` +### Feature Development Plan + +| Status |Task | +|:---:|:---:| +|✅| web api template | +|✅| web site with htmlx template | +|✅|with sqlx template| +|✅|basic midware | +|✅|suport sqlite,pgsql,mysql| +||with seaorm template| +|| better web site | +|| with diese template| +|| with Rbatis template| ## License This project is licensed under the MIT OR Apache-2.0 License. From c5966c0994c89f7846a742d7d07576631bfc8d74 Mon Sep 17 00:00:00 2001 From: Fankai Liu Date: Tue, 10 Oct 2023 22:11:11 +0800 Subject: [PATCH 17/19] release of new versions --- Cargo.lock | 2 +- Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 17fdbac..b9e1a2f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -798,7 +798,7 @@ checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" [[package]] name = "salvo-cli" -version = "0.1.6" +version = "0.1.7" dependencies = [ "ansi_term", "anyhow", diff --git a/Cargo.toml b/Cargo.toml index 236caed..afadce3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "salvo-cli" -version = "0.1.6" +version = "0.1.7" edition = "2021" authors = ["Fankai Liu liufankai137@outlook.com"] keywords = ["salvo", "cli","template"] From 9d0eaebbe2538991bed4f3c1096bd5742adf277a Mon Sep 17 00:00:00 2001 From: Fankai Liu Date: Thu, 12 Oct 2023 22:19:10 +0800 Subject: [PATCH 18/19] Improvement Tips --- Cargo.lock | 2 +- Cargo.toml | 2 +- locales/code_comment.yml | 52 +++++++++++++------ .../data/{init_sql.sql => init_sql_sql.hbs} | 1 + src/utils/create_project.rs | 6 ++- 5 files changed, 42 insertions(+), 21 deletions(-) rename src/template/data/{init_sql.sql => init_sql_sql.hbs} (84%) diff --git a/Cargo.lock b/Cargo.lock index b9e1a2f..0a24801 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -798,7 +798,7 @@ checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" [[package]] name = "salvo-cli" -version = "0.1.7" +version = "0.1.8" dependencies = [ "ansi_term", "anyhow", diff --git a/Cargo.toml b/Cargo.toml index afadce3..4ab71af 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "salvo-cli" -version = "0.1.7" +version = "0.1.8" edition = "2021" authors = ["Fankai Liu liufankai137@outlook.com"] keywords = ["salvo", "cli","template"] diff --git a/locales/code_comment.yml b/locales/code_comment.yml index a5f48df..b5a14ef 100644 --- a/locales/code_comment.yml +++ b/locales/code_comment.yml @@ -437,20 +437,38 @@ create_success_sqlx_sqlite: el: 🎯 Δημιουργήθηκε η προεπιλεγμένη βάση δεδομένων στο `/data/demo.db`. \n Μετά την εκτέλεσή του, μπορείτε να αποκτήσετε πρόσβαση στο /login με το όνομα χρήστη:'zhangsan' και τον κωδικό πρόσβασης:'123'. da: 🎯 Standarddatabasen er oprettet i `/data/demo.db`. \n Efter at have kørt det, kan du få adgang til /login med standardbrugernavnet:'zhangsan' og adgangskoden:'123'. create_success_mysql_or_pgsql: - en: Modify the database connection string in .env and config/config.toml\n Run this SQL in the database to add default data.\n After running, you can access /login with default username:'zhangsan' and password:'123'. - zh_CN: 修改.env和config/config.toml中的数据库连接字符串\n 在数据库中运行此SQL以添加默认数据。\n 运行后,您可以使用默认用户名:'zhangsan'和密码:'123'访问/login。 - zh_TW: 修改.env和config/config.toml中的資料庫連接字串\n 在資料庫中運行此SQL以添加預設資料。\n 運行後,您可以使用預設用戶名:'zhangsan'和密碼:'123'訪問/login。 - fr: Modifiez la chaîne de connexion à la base de données dans .env et config/config.toml\n Exécutez ce SQL dans la base de données pour ajouter des données par défaut.\n Après l'exécution, vous pouvez accéder à /login avec le nom d'utilisateur par défaut:'zhangsan' et le mot de passe:'123'. - ja: .envとconfig/config.tomlのデータベース接続文字列を変更します\n データベースでこのSQLを実行してデフォルトデータを追加します。\n 実行後、デフォルトのユーザー名:'zhangsan'とパスワード:'123'で/loginにアクセスできます。 - es: Modifique la cadena de conexión de la base de datos en .env y config/config.toml\n Ejecute este SQL en la base de datos para agregar datos predeterminados.\n Después de ejecutarlo, puede acceder a /login con el nombre de usuario predeterminado:'zhangsan' y la contraseña:'123'. - de: Ändern Sie die Verbindungszeichenfolge zur Datenbank in .env und config/config.toml\n Führen Sie diesen SQL in der Datenbank aus, um Standarddaten hinzuzufügen.\n Nach dem Ausführen können Sie mit dem Standardbenutzernamen:'zhangsan' und dem Passwort:'123' auf /login zugreifen. - ru: Измените строку подключения к базе данных в .env и config/config.toml\n Запустите этот SQL в базе данных, чтобы добавить данные по умолчанию.\n После запуска вы можете получить доступ к /login с именем пользователя по умолчанию:'zhangsan' и паролем:'123'. - it: Modifica la stringa di connessione al database in .env e config/config.toml\n Esegui questo SQL nel database per aggiungere dati predefiniti.\n Dopo l'esecuzione, è possibile accedere a /login con l'username predefinito:'zhangsan' e la password:'123'. - pt: Modifique a string de conexão do banco de dados em .env e config/config.toml\n Execute este SQL no banco de dados para adicionar dados padrão.\n Após a execução, você pode acessar /login com o nome de usuário padrão:'zhangsan' e a senha:'123'. - ko: .env 및 config/config.toml에서 데이터베이스 연결 문자열을 수정하십시오.\n 데이터베이스에서이 SQL을 실행하여 기본 데이터를 추가하십시오.\n 실행 후 기본 사용자 이름:'zhangsan'과 비밀번호:'123'으로 /login에 액세스 할 수 있습니다. - no: Endre database-tilkoblingsstrengen i .env og config/config.toml\n Kjør denne SQL-en i databasen for å legge til standarddata.\n Etter å ha kjørt det, kan du få tilgang til /login med standard brukernavn:'zhangsan' og passord:'123'. - is: Breyttu tengingu strenginn í .env og config/config.toml\n Keyrið þetta SQL í gagnagrunninum til að bæta við sjálfgefnum gögnum.\n Eftir að hafa keyrt það, getur þú fengið aðgang að /login með sjálfgefnum notandanafni:'zhangsan' og lykilorði:'123'. - uk: Змініть рядок підключення до бази даних в .env та config/config.toml\n Виконайте цей SQL в базі даних, щоб додати дані за замовчуванням.\n Після запуску ви можете отримати доступ до /login за допомогою імені користувача за замовчуванням:'zhangsan' та пароля:'123'. - th: แก้ไขสตริงการเชื่อมต่อฐานข้อมูลใน .env และ config/config.toml\n รัน SQL นี้ในฐานข้อมูลเพื่อเพิ่มข้อมูลเริ่มต้น\n หลังจากเรียกใช้งาน คุณสามารถเข้าถึง /login ด้วยชื่อผู้ใช้เริ่มต้น:'zhangsan' และรหัสผ่าน:'123'. - el: Τροποποιήστε τη συμβολοσειρά σύνδεσης της βάσης δεδομένων στα .env και config/config.toml\n Εκτελέστε αυτή την SQL στη βάση δεδομένων για να προσθέσετε προεπιλεγμένα δεδομένα.\n Μετά την εκτέλεση, μπορείτε να έχετε πρόσβαση στο /login με προεπιλεγμένο όνομα χρήστη:'zhangsan' και κωδικό πρόσβασης:'123'. - da: Rediger databaseforbindelsesstrengen i .env og config/config.toml\n Kør denne SQL i databasen for at tilføje standarddata.\n Efter at have kørt det, kan du få adgang til /login med standardbrugernavnet:'zhangsan' og adgangskoden:'123'. \ No newline at end of file + en: 📊 Follow the instructions in the data/init_sql.sql file to complete the initialization of data + zh_CN: 📊 按照data/init_sql.sql文件中的说明完成数据初始化 + zh_TW: 📊 按照data/init_sql.sql文件中的說明完成資料初始化 + fr: 📊 Suivez les instructions du fichier data/init_sql.sql pour terminer l'initialisation des données + ja: 📊 data/init_sql.sqlファイルの指示に従って、データの初期化を完了します + es: 📊 Siga las instrucciones del archivo data/init_sql.sql para completar la inicialización de datos + de: 📊 Befolgen Sie die Anweisungen in der Datei data/init_sql.sql, um die Initialisierung der Daten abzuschließen + ru: 📊 Следуйте инструкциям в файле data/init_sql.sql, чтобы завершить инициализацию данных + it: 📊 Seguire le istruzioni nel file data/init_sql.sql per completare l'inizializzazione dei dati + pt: 📊 Siga as instruções no arquivo data/init_sql.sql para concluir a inicialização dos dados + ko: 📊 data/init_sql.sql 파일의 지침에 따라 데이터 초기화를 완료하십시오 + no: 📊 Følg instruksjonene i data/init_sql.sql-filen for å fullføre initialiseringen av data + is: 📊 Fylgdu leiðbeiningunum í skránni data/init_sql.sql til að ljúka upphafsstillingu gagna + uk: 📊 Дотримуйтесь інструкцій у файлі data/init_sql.sql, щоб завершити ініціалізацію даних + th: 📊 ปฏิบัติตามคำแนะนำในไฟล์ data/init_sql.sql เพื่อเสร็จสิ้นการเริ่มต้นข้อมูล + el: 📊 Ακολουθήστε τις οδηγίες στο αρχείο data/init_sql.sql για να ολοκληρώσετε την αρχικοποίηση των δεδομένων + da: 📊 Følg instruktionerne i data/init_sql.sql-filen for at fuldføre initialiseringen af data +create_success_mysql_or_pgsql_fist_use: + en: -- Please first use cargo install sqlx-cli \n -- Modify the database connection string in .env and config/config.toml \n -- Then execute sqlx database create to create the database \n -- Execute sqlx migrate run to restore the database, run the following SQL in the database to add default data. \n -- After running, you can access /login with the default username:'zhangsan' and password:'123'. + zh_CN: -- 请先使用 cargo install sqlx-cli \n -- 修改.env和config/config.toml中的数据库连接字符串 \n -- 然后执行sqlx database create 创建数据库 \n -- 执行sqlx migrate run 还原数据库, 在数据库中运以下SQL以添加默认数据。 \n -- 运行后,您可以使用默认用户名:'zhangsan'和密码:'123'访问/login。 + zh_TW: -- 首先,使用 cargo install sqlx-cli \n -- 修改.env和config/config.toml中的數據庫連接字符串 \n -- 然後執行sqlx database create 創建數據庫 \n -- 執行sqlx migrate run 恢复數據庫, 在數據庫中運行以下SQL以添加默認數據。 \n -- 運行後,您可以使用默認用戶名:'zhangsan'和密碼:'123'訪問/login。 + fr: -- D'abord, utilisez cargo install sqlx-cli \n -- Modifiez la chaîne de connexion à la base de données dans .env et config/config.toml \n -- Ensuite, exécutez sqlx database create pour créer la base de données \n -- Exécutez sqlx migrate run pour restaurer la base de données, exécutez le SQL suivant dans la base de données pour ajouter les données par défaut. \n -- Après l'exécution, vous pouvez accéder à /login avec le nom d'utilisateur par défaut:'zhangsan' et le mot de passe:'123'. + ja: -- まず、cargo install sqlx-cliを使用してください \n -- .envとconfig/config.tomlのデータベース接続文字列を変更します \n -- 次に、sqlx database createを実行してデータベースを作成します \n -- sqlx migrate runを実行してデータベースを復元し、次のSQLをデータベースで実行してデフォルトデータを追加します。 \n -- 実行後、デフォルトのユーザー名:'zhangsan'とパスワード:'123'で/loginにアクセスできます。 + es: -- Primero, use cargo install sqlx-cli \n -- Modifique la cadena de conexión de la base de datos en .env y config/config.toml \n -- Luego, ejecute sqlx database create para crear la base de datos \n -- Ejecute sqlx migrate run para restaurar la base de datos, ejecute el siguiente SQL en la base de datos para agregar datos predeterminados. \n -- Después de ejecutarlo, puede acceder a /login con el nombre de usuario predeterminado:'zhangsan' y la contraseña:'123'. + de: -- Bitte verwenden Sie zuerst cargo install sqlx-cli \n -- Ändern Sie die Verbindungszeichenfolge zur Datenbank in .env und config/config.toml \n -- Führen Sie dann sqlx database create aus, um die Datenbank zu erstellen \n -- Führen Sie sqlx migrate run aus, um die Datenbank wiederherzustellen. Führen Sie den folgenden SQL in der Datenbank aus, um Standarddaten hinzuzufügen. \n -- Nach dem Ausführen können Sie mit dem Standardbenutzernamen:'zhangsan' und dem Passwort:'123' auf /login zugreifen. + ru: -- Пожалуйста, сначала используйте cargo install sqlx-cli \n -- Измените строку подключения к базе данных в .env и config/config.toml \n -- Затем выполните sqlx database create, чтобы создать базу данных \n -- Выполните sqlx migrate run, чтобы восстановить базу данных, выполните следующий SQL в базе данных, чтобы добавить данные по умолчанию. \n -- После запуска вы можете получить доступ к /login с именем пользователя по умолчанию:'zhangsan' и паролем:'123'. + it: -- Si prega di utilizzare prima cargo install sqlx-cli \n -- Modifica la stringa di connessione al database in .env e config/config.toml \n -- Quindi esegui sqlx database create per creare il database \n -- Esegui sqlx migrate run per ripristinare il database, esegui il seguente SQL nel database per aggiungere dati predefiniti. \n -- Dopo l'esecuzione, è possibile accedere a /login con l'username predefinito:'zhangsan' e la password:'123'. + pt: -- Por favor, use primeiro cargo install sqlx-cli \n -- Modifique a string de conexão do banco de dados em .env e config/config.toml \n -- Em seguida, execute sqlx database create para criar o banco de dados \n -- Execute sqlx migrate run para restaurar o banco de dados, execute o seguinte SQL no banco de dados para adicionar dados padrão. \n -- Após a execução, você pode acessar /login com o nome de usuário padrão:'zhangsan' e a senha:'123'. + ko: -- 먼저 cargo install sqlx-cli를 사용하십시오. \n -- .env 및 config/config.toml에서 데이터베이스 연결 문자열을 수정하십시오. \n -- 그런 다음 sqlx database create를 실행하여 데이터베이스를 만듭니다. \n -- 데이터베이스를 복원하려면 sqlx migrate run을 실행하고 다음 SQL을 데이터베이스에서 실행하여 기본 데이터를 추가하십시오. \n -- 실행 후 기본 사용자 이름:'zhangsan'과 비밀번호:'123'으로 /login에 액세스 할 수 있습니다. + no: -- Vennligst bruk først cargo install sqlx-cli \n -- Endre database-tilkoblingsstrengen i .env og config/config.toml \n -- Kjør deretter sqlx database create for å opprette databasen \n -- Kjør sqlx migrate run for å gjenopprette databasen, kjør følgende SQL i databasen for å legge til standarddata. \n -- Etter å ha kjørt det, kan du få tilgang til /login med standard brukernavn:'zhangsan' og passord:'123'. + is: -- Vinsamlegast notaðu fyrst cargo install sqlx-cli \n -- Breyttu tengingu strenginn í .env og config/config.toml \n -- Keyrið svo sqlx database create til að búa til gagnagrunninn \n -- Keyrið sqlx migrate run til að endurheimta gagnagrunninn, keyrið eftirfarandi SQL í gagnagrunninum til að bæta við sjálfgefnum gögnum. \n -- Eftir að hafa keyrt það, getur þú fengið aðgang að /login með sjálfgefnum notandanafni:'zhangsan' og lykilorði:'123'. + uk: -- Будь ласка, спочатку використовуйте cargo install sqlx-cli \n -- Змініть рядок підключення до бази даних в .env та config/config.toml \n -- Потім виконайте sqlx database create, щоб створити базу даних \n -- Виконайте sqlx migrate run, щоб відновити базу даних, виконайте наступний SQL в базі даних, щоб додати дані за замовчуванням. \n -- Після запуску ви можете отримати доступ до /login за допомогою імені користувача за замовчуванням:'zhangsan' та пароля:'123'. + th: -- โปรดใช้ก่อน cargo install sqlx-cli \n -- แก้ไขสตริงการเชื่อมต่อฐานข้อมูลใน .env และ config/config.toml \n -- จากนั้นเรียกใช้ sqlx database create เพื่อสร้างฐานข้อมูล \n -- รัน sqlx migrate run เพื่อกู้คืนฐานข้อมูล รัน SQL ต่อไปนี้ในฐานข้อมูลเพื่อเพิ่มข้อมูลเริ่มต้น \n -- หลังจากเรียกใช้งาน คุณสามารถเข้าถึง /login ด้วยชื่อผู้ใช้เริ่มต้น:'zhangsan' และรหัสผ่าน:'123'. + el: -- Παρακαλώ χρησιμοποιήστε πρώτα cargo install sqlx-cli \n -- Τροποποιήστε τη συμβολοσειρά σύνδεσης της βάσης δεδομένων στα .env και config/config.toml \n -- Στη συνέχεια, εκτελέστε sqlx database create για να δημιουργήσετε τη βάση δεδομένων \n -- Εκτελέστε το sqlx migrate run για να επαναφέρετε τη βάση δεδομένων, εκτελέστε το ακόλουθο SQL στη βάση δεδομένων για να προσθέσετε προεπιλεγμένα δεδομένα. \n -- Μετά την εκτέλεση, μπορείτε να έχετε πρόσβαση στο /login με προεπιλεγμένο όνομα χρήστη:'zhangsan' και κωδικό πρόσβασης:'123'. + da: -- Brug venligst først cargo install sqlx-cli \n -- Rediger databaseforbindelsesstrengen i .env og config/config.toml \n -- Kør derefter sqlx database create for at oprette databasen \n -- Kør sqlx migrate run for at gendanne databasen, kør følgende SQL i databasen for at tilføje standarddata. \n -- Efter at have kørt det, kan du få adgang til /login med standardbrugernavnet:'zhangsan' og adgangskoden:'123'. \ No newline at end of file diff --git a/src/template/data/init_sql.sql b/src/template/data/init_sql_sql.hbs similarity index 84% rename from src/template/data/init_sql.sql rename to src/template/data/init_sql_sql.hbs index a7798d5..9367c2b 100644 --- a/src/template/data/init_sql.sql +++ b/src/template/data/init_sql_sql.hbs @@ -1,3 +1,4 @@ +{{create_success_mysql_or_pgsql_fist_use}} BEGIN; INSERT INTO "users" ("id", "username", "password") VALUES ('cdd0e080-5bb1-4442-b6f7-2ba60dbd0555', 'zhangsan', '$argon2id$v=19$m=19456,t=2,p=1$rcosL5pOPdA2c7i4ZuLA4Q$s0JGh78UzMmu1qZMpVUA3b8kWYLXcZhw7uBfwhYDJ4A'); COMMIT; diff --git a/src/utils/create_project.rs b/src/utils/create_project.rs index b136ff0..68f14b8 100644 --- a/src/utils/create_project.rs +++ b/src/utils/create_project.rs @@ -112,6 +112,7 @@ fn write_project_file( "yes":t!("yes"), "cancel":t!("cancel"), "operation":t!("operation"), + "create_success_mysql_or_pgsql_fist_use":t!("create_success_mysql_or_pgsql_fist_use").replace(r"\n", "\n"), }); if is_sqlx { // Add sqlx dependencies @@ -362,9 +363,10 @@ fn write_project_file( } else{ //data/init_sql.sql - let init_sql_bytes = include_bytes!("../template/data/init_sql.sql"); + let init_sql_templte = include_str!("../template/data/init_sql_sql.hbs"); + let init_sql_rendered = handlebars.render_template(init_sql_templte, &data)?; let mut init_sql_file = File::create(data_path.join("init_sql.sql"))?; - init_sql_file.write_all(init_sql_bytes)?; + init_sql_file.write_all(init_sql_rendered.as_bytes())?; } //migrations let migrations_path = project_path.join("migrations"); From aaf84195ecee7387e97b616aae2571deab093ab3 Mon Sep 17 00:00:00 2001 From: Fankai Liu Date: Fri, 13 Oct 2023 23:08:55 +0800 Subject: [PATCH 19/19] add seaorm --- src/utils/get_selection.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/get_selection.rs b/src/utils/get_selection.rs index 81e81d6..3893bc7 100644 --- a/src/utils/get_selection.rs +++ b/src/utils/get_selection.rs @@ -35,7 +35,7 @@ pub fn get_user_selected() -> Result> { let db_conn_types = &[ t!("db_conn_types_sqlx"), // t!("db_conn_types_diesel"), - // t!("db_conn_types_sea_orm"), + t!("db_conn_types_sea_orm"), // t!("db_conn_types_rbatis"), t!("db_conn_types_nothing"), // "custom",