From 76785af4dc801e587cba904d013977acbd854c9b Mon Sep 17 00:00:00 2001 From: Manuel Cillero Date: Thu, 10 Mar 2022 00:10:48 +0100 Subject: [PATCH] =?UTF-8?q?A=C3=B1ade=20configuraci=C3=B3n=20y=20conexi?= =?UTF-8?q?=C3=B3n=20a=20la=20base=20de=20datos?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Cargo.toml | 12 +++ config/settings.default.toml | 14 ++++ src/base/module/admin/summary.rs | 3 +- src/config.rs | 19 +++++ src/core/server/app.rs | 138 +++++++++++++++++++++++++++++++ src/core/server/dbconn.rs | 8 ++ src/core/server/main.rs | 76 ----------------- src/core/server/mod.rs | 6 +- src/database.rs | 1 + src/lib.rs | 3 + src/locale.rs | 6 +- src/main.rs | 6 +- src/prelude.rs | 1 + 13 files changed, 210 insertions(+), 83 deletions(-) create mode 100644 src/core/server/app.rs create mode 100644 src/core/server/dbconn.rs delete mode 100644 src/core/server/main.rs create mode 100644 src/database.rs diff --git a/Cargo.toml b/Cargo.toml index 6d9fa877..d28a0e33 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,6 +29,7 @@ once_cell = "1.9.0" figlet-rs = "0.1.3" config_rs = { package = "config", version = "0.11.0", features = ["toml"] } +url = "2.2.2" tracing = "0.1" tracing-appender = "0.2" @@ -47,9 +48,20 @@ downcast-rs = "1.2.0" serde = { version = "1.0", features = ["derive"] } +[dependencies.sea-orm] +version = "0.6" +features = ["macros", "debug-print", "runtime-async-std-native-tls"] +default-features = false + [build-dependencies] actix-web-static-files = "3.0.5" +[features] +default = ["sea-orm/sqlx-mysql"] +mysql = ["sea-orm/sqlx-mysql"] +postgres = ["sea-orm/sqlx-postgres"] +sqlite = ["sea-orm/sqlx-sqlite"] + [lib] name = "pagetop" diff --git a/config/settings.default.toml b/config/settings.default.toml index 3a4fb0ff..f22868d2 100644 --- a/config/settings.default.toml +++ b/config/settings.default.toml @@ -23,6 +23,20 @@ prefix = "tracing.log" # Presentación de las trazas: "Json", "Full", "Compact" o "Pretty". format = "Json" +[database] +# Ajustes para conectar con la base de datos. +# El tipo de base de datos es una característica de compilación. +# Nombre (mysql/postgres) o referencia (sqlite) de la base de datos. +db_name = "dbname" +# Usuario y contraseña (mysql/postgres). +db_user = "dbuser" +db_pass = "dbpass" +# Servidor (mysql/postgres) y puerto predeterminado (0 para 3306 ó 5432). +db_host = "localhost" +db_port = 0 +# Número máximo de conexiones habilitadas. +max_pool_size = 5 + [webserver] # Configuración del servidor web. bind_address = "localhost" diff --git a/src/base/module/admin/summary.rs b/src/base/module/admin/summary.rs index 2f889e9e..cee26c22 100644 --- a/src/base/module/admin/summary.rs +++ b/src/base/module/admin/summary.rs @@ -1,8 +1,9 @@ use crate::prelude::*; +use super::l; pub async fn summary() -> server::Result { let top_menu = Menu::prepare() - .add(MenuItem::label("Opción 1")) + .add(MenuItem::label(l("module_fullname").as_str())) .add(MenuItem::link("Opción 2", "https://www.google.es")) .add(MenuItem::link_blank("Opción 3", "https://www.google.es")) .add(MenuItem::submenu("Submenú 1", Menu::prepare() diff --git a/src/config.rs b/src/config.rs index bd80bd76..c6232c7a 100644 --- a/src/config.rs +++ b/src/config.rs @@ -87,6 +87,16 @@ pub struct Log { pub format : String, } +#[derive(Debug, Deserialize)] +pub struct Database { + pub db_name : String, + pub db_user : String, + pub db_pass : String, + pub db_host : String, + pub db_port : u16, + pub max_pool_size : u32, +} + #[derive(Debug, Deserialize)] pub struct Webserver { pub bind_address : String, @@ -97,6 +107,7 @@ pub struct Webserver { pub struct Settings { pub app : App, pub log : Log, + pub database : Database, pub webserver : Webserver, } @@ -121,6 +132,14 @@ Ajustes globales y valores predeterminados para las secciones *\[app\]*, "log.prefix" => "tracing.log", "log.format" => "json", + // [database] + "database.db_name" => "dbname", + "database.db_user" => "dbuser", + "database.db_pass" => "dbpass", + "database.db_host" => "localhost", + "database.db_port" => 0, + "database.max_pool_size" => 5, + // [webserver] "webserver.bind_address" => "localhost", "webserver.bind_port" => 8088 diff --git a/src/core/server/app.rs b/src/core/server/app.rs new file mode 100644 index 00000000..3861c412 --- /dev/null +++ b/src/core/server/app.rs @@ -0,0 +1,138 @@ +use crate::{Lazy, base, locale, trace}; +use crate::config::SETTINGS; +use crate::core::{Server, global, server}; +use crate::core::theme::register_theme; +use crate::core::module::register_module; + +use std::io::Error; +use actix_web::middleware::normalize::{NormalizePath, TrailingSlash}; + +pub struct Application { + server: Server, +} + +impl Application { + pub async fn build(bootstrap: Option) -> Result { + // Imprime rótulo (opcional) de bienvenida. + if SETTINGS.app.startup_banner.to_lowercase() != "off" { + let figfont = figlet_rs::FIGfont::from_content( + match SETTINGS.app.startup_banner.to_lowercase().as_str() { + "slant" => include_str!("figfonts/slant.flf"), + "small" => include_str!("figfonts/small.flf"), + "speed" => include_str!("figfonts/speed.flf"), + "starwars" => include_str!("figfonts/starwars.flf"), + _ => { + println!( + "FIGfont \"{}\" not found for banner. {}. {}.", + SETTINGS.app.startup_banner, + "Using \"Small\"", + "Check the settings file", + ); + include_str!("figfonts/small.flf") + } + } + ).unwrap(); + println!("\n{} {}\n\n Powered by PageTop {}\n", + figfont.convert(&SETTINGS.app.name).unwrap(), + &SETTINGS.app.description, + env!("CARGO_PKG_VERSION") + ); + } + + // Inicia registro de trazas y eventos. + Lazy::force(&server::tracing::TRACING); + + // Valida el identificador de idioma. + Lazy::force(&locale::LANGID); + + // Inicializa la conexión con la base de datos. + trace::info!( + "Connecting to database \"{}\" with a pool of {} connections.", + &SETTINGS.database.db_name, + &SETTINGS.database.max_pool_size + ); + + #[cfg(any(feature = "default", feature = "mysql"))] + let db_uri = format!( + "mysql://{}/{}", + &SETTINGS.database.db_host, + &SETTINGS.database.db_name + ); + + #[cfg(feature = "postgres")] + let db_uri = format!( + "postgres://{}/{}", + &SETTINGS.database.db_host, + &SETTINGS.database.db_name + ); + + #[cfg(feature = "sqlite")] + let db_uri = format!("sqlite://{}", &SETTINGS.database.db_name); + + let mut uri = url::Url::parse(&db_uri).unwrap(); + + // https://github.com/launchbadge/sqlx/issues/1624 + + #[cfg(not(feature = "sqlite"))] + uri.set_username(&SETTINGS.database.db_user.as_str()).unwrap(); + + #[cfg(not(feature = "sqlite"))] + uri.set_password(Some(&SETTINGS.database.db_pass.as_str())).unwrap(); + + #[cfg(not(feature = "sqlite"))] + if SETTINGS.database.db_port != 0 { + uri.set_port(Some(SETTINGS.database.db_port)).unwrap(); + } + + let mut db_options = sea_orm::ConnectOptions::new(uri.to_string()); + db_options.max_connections(SETTINGS.database.max_pool_size); + + let mut db_conn = server::dbconn::DBCONN.write().unwrap(); + *db_conn = Some( + sea_orm::Database::connect::( + db_options.into() + ) + .await + .expect("Failed to connect to database") + ); + + // Registra los temas predefinidos. + register_theme(&base::theme::aliner::AlinerTheme); + register_theme(&base::theme::minimal::MinimalTheme); + register_theme(&base::theme::bootsier::BootsierTheme); + + // Registra los módulos predeterminados. + register_module(&base::module::admin::AdminModule); + register_module(&base::module::user::UserModule); + + // Ejecuta la función de inicio de la aplicación. + if bootstrap != None { + trace::debug!("Calling application bootstrap"); + let _ = &(bootstrap.unwrap())(); + } + + // Registra el módulo para la página de inicio de PageTop. + // Al ser el último, puede sobrecargarse con la función de inicio. + register_module(&base::module::homepage::HomepageModule); + + // Prepara el servidor web. + let server = server::HttpServer::new(|| { + server::App::new() + .wrap(tracing_actix_web::TracingLogger) + .wrap(NormalizePath::new(TrailingSlash::Trim)) + .configure(&global::themes) + .configure(&global::modules) + }) + .bind(format!("{}:{}", + &SETTINGS.webserver.bind_address, + &SETTINGS.webserver.bind_port + ))? + .run(); + + Ok(Self { server }) + } + + pub fn run(self) -> Result { + Ok(self.server) + } +} diff --git a/src/core/server/dbconn.rs b/src/core/server/dbconn.rs new file mode 100644 index 00000000..e80629fc --- /dev/null +++ b/src/core/server/dbconn.rs @@ -0,0 +1,8 @@ +use crate::Lazy; +use crate::database::DatabaseConnection; + +use std::sync::RwLock; + +pub static DBCONN: Lazy>> = Lazy::new(|| { + RwLock::new(None) +}); diff --git a/src/core/server/main.rs b/src/core/server/main.rs deleted file mode 100644 index b6800142..00000000 --- a/src/core/server/main.rs +++ /dev/null @@ -1,76 +0,0 @@ -use crate::{Lazy, base, locale, trace}; -use crate::config::SETTINGS; -use crate::core::{Server, global, server}; -use crate::core::theme::register_theme; -use crate::core::module::register_module; - -use actix_web::middleware::normalize; - -pub fn run(bootstrap: Option) -> Result { - // Imprime el rótulo (opcional) de bienvenida. - if SETTINGS.app.startup_banner.to_lowercase() != "off" { - let figfont = figlet_rs::FIGfont::from_content( - match SETTINGS.app.startup_banner.to_lowercase().as_str() { - "slant" => include_str!("figfonts/slant.flf"), - "small" => include_str!("figfonts/small.flf"), - "speed" => include_str!("figfonts/speed.flf"), - "starwars" => include_str!("figfonts/starwars.flf"), - _ => { - println!( - "FIGfont \"{}\" not found for banner. {}. {}.", - SETTINGS.app.startup_banner, - "Using \"Small\"", - "Check the settings file", - ); - include_str!("figfonts/small.flf") - } - } - ).unwrap(); - println!("\n{} {}\n\n Powered by PageTop {}\n", - figfont.convert(&SETTINGS.app.name).unwrap(), - &SETTINGS.app.description, - env!("CARGO_PKG_VERSION") - ); - } - - // Inicia registro de trazas y eventos. - Lazy::force(&server::tracing::TRACING); - - // Asigna identificador de idioma. - Lazy::force(&locale::LANGID); - - // Registra los temas predefinidos. - register_theme(&base::theme::aliner::AlinerTheme); - register_theme(&base::theme::minimal::MinimalTheme); - register_theme(&base::theme::bootsier::BootsierTheme); - - // Registra los módulos predeterminados. - register_module(&base::module::admin::AdminModule); - register_module(&base::module::user::UserModule); - - // Ejecuta la función de inicio de la aplicación. - if bootstrap != None { - trace::debug!("Calling application bootstrap"); - let _ = &(bootstrap.unwrap())(); - } - - // Registra el módulo para la página de inicio de PageTop. - // Al ser el último, puede sobrecargarse en la función de arranque. - register_module(&base::module::homepage::HomepageModule); - - - // Inicializa el servidor web. - let server = server::HttpServer::new(|| { - server::App::new() - .wrap(tracing_actix_web::TracingLogger) - .wrap(normalize::NormalizePath::new(normalize::TrailingSlash::Trim)) - .configure(&global::themes) - .configure(&global::modules) - }) - .bind(format!("{}:{}", - &SETTINGS.webserver.bind_address, - &SETTINGS.webserver.bind_port - ))? - .run(); - Ok(server) -} diff --git a/src/core/server/mod.rs b/src/core/server/mod.rs index 7db53759..8699b8d1 100644 --- a/src/core/server/mod.rs +++ b/src/core/server/mod.rs @@ -4,5 +4,7 @@ pub use actix_web::{ mod tracing; -mod main; -pub use main::run; +mod dbconn; + +mod app; +pub use app::Application; diff --git a/src/database.rs b/src/database.rs new file mode 100644 index 00000000..cd70e401 --- /dev/null +++ b/src/database.rs @@ -0,0 +1 @@ +pub use sea_orm::DatabaseConnection; diff --git a/src/lib.rs b/src/lib.rs index 7d17db44..a01548ba 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -10,8 +10,11 @@ pub use once_cell::sync::Lazy; pub mod config; // Gestión de la configuración. pub mod trace; // Registro de trazas y eventos de la aplicación. pub mod locale; // Localización. +pub mod database; // Acceso a la base de datos. pub mod core; // Servidor web y sistemas para Temas, Módulos y Respuestas. pub mod base; // Temas, Módulos y Componentes base. pub mod util; // Macros y funciones útiles. pub mod prelude; // Re-exporta recursos comunes. + +pub use crate::core::server::Application; diff --git a/src/locale.rs b/src/locale.rs index e3639858..07cb0d8e 100644 --- a/src/locale.rs +++ b/src/locale.rs @@ -44,12 +44,12 @@ macro_rules! localize { } #[allow(dead_code)] - pub fn l(key: &str) -> String { + fn l(key: &str) -> String { LOCALES.lookup(&LANGID, key) } #[allow(dead_code)] - pub fn t( + fn t( key: &str, args: &std::collections::HashMap ) -> String { @@ -57,7 +57,7 @@ macro_rules! localize { } #[allow(dead_code)] - pub fn e( + fn e( key: &str, args: &std::collections::HashMap ) -> crate::core::theme::PreEscaped { diff --git a/src/main.rs b/src/main.rs index 8559a909..30df2f36 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,8 @@ #[actix_web::main] async fn main() -> std::io::Result<()> { - pagetop::core::server::run(None)?.await + // ``` + // let app = pagetop::Application::build(None).await?; + // app.run()?.await + // ``` + pagetop::Application::build(None).await?.run()?.await } diff --git a/src/prelude.rs b/src/prelude.rs index 94630400..6ca17039 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -6,6 +6,7 @@ pub use crate::util; pub use crate::config::SETTINGS; pub use crate::trace; pub use crate::localize; +pub use crate::database; pub use crate::core::theme::*; pub use crate::core::module::*;