diff --git a/config/default.toml b/config/default.toml index 26676acb..33280fbd 100644 --- a/config/default.toml +++ b/config/default.toml @@ -9,12 +9,10 @@ language = "es-ES" theme = "Bootsier" [database] +db_type = "mysql" db_name = "drust" db_user = "drust" db_pass = "DrU__#3T" [log] -tracing = "Info" -#Info,actix_server::builder=Error,tracing_actix_web=Warn" -rolling = "Stdout" -format = "Compact" +tracing = "Info,sqlx::query=Warn" diff --git a/drust/Cargo.toml b/drust/Cargo.toml index 0a579e5f..cfa4a264 100644 --- a/drust/Cargo.toml +++ b/drust/Cargo.toml @@ -13,7 +13,11 @@ homepage = "https://suitepro.cillero.es/projects/drust" repository = "https://gitlab.com/manuelcillero/drust" [dependencies] -pagetop = { path = "../pagetop" } actix-web = "3.3.3" maud = { version = "0.23.0" } serde = { version = "1.0", features = ["derive"] } + +[dependencies.pagetop] +path = "../pagetop" +features = ["mysql"] +default-features = false diff --git a/pagetop/Cargo.toml b/pagetop/Cargo.toml index 278b6ba0..0b6c5d7d 100644 --- a/pagetop/Cargo.toml +++ b/pagetop/Cargo.toml @@ -36,6 +36,7 @@ config_rs = { package = "config", version = "0.11.0", features = ["toml"] } tracing = "0.1" tracing-appender = "0.2" tracing-subscriber = { version = "0.3", features = ["json", "env-filter"] } +tracing-unwrap = { version = "0.9", default-features = false } tracing-actix-web = "0.2" fluent-templates = "0.6.1" @@ -53,16 +54,19 @@ serde = { version = "1.0", features = ["derive"] } version = "0.6" features = ["debug-print", "macros", "runtime-async-std-native-tls"] default-features = false +optional = true [dependencies.sea-schema] version = "0.5" features = ["debug-print", "migration"] default-features = false +optional = true [features] -default = ["mysql"] -mysql = ["sea-orm/sqlx-mysql"] -postgres = ["sea-orm/sqlx-postgres"] +default = [] +mysql = ["sea-orm", "sea-schema", "sea-orm/sqlx-mysql"] +postgres = ["sea-orm", "sea-schema", "sea-orm/sqlx-postgres"] +sqlite = ["sea-orm", "sea-schema", "sea-orm/sqlx-sqlite"] [build-dependencies] actix-web-static-files = "3.0.5" diff --git a/pagetop/config/settings.default.toml b/pagetop/config/settings.default.toml index f22868d2..b9c329cf 100644 --- a/pagetop/config/settings.default.toml +++ b/pagetop/config/settings.default.toml @@ -15,24 +15,26 @@ startup_banner = "Small" # Ejemplos: "Error,actix_server::builder=Info,tracing_actix_web=Debug". tracing = "Info" # En terminal ("Stdout") o archivos "Daily", "Hourly", "Minutely" o "Endless". -rolling = "Daily" +rolling = "Stdout" # Directorio para los archivos de traza (si rolling != "Stdout"). path = "log" # Prefijo para los archivos de traza (si rolling != "Stdout"). prefix = "tracing.log" -# Presentación de las trazas: "Json", "Full", "Compact" o "Pretty". -format = "Json" +# Presentación de las trazas: "Full", "Compact", "Pretty" o "Json". +format = "Full" [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). +# Conecta con una base de datos (opcional). +# Tipo de la base de datos (mysql, postgres ó sqlite). +db_type = "" +# Nombre (para mysql/postgres) o referencia (para sqlite) de la base de datos. +db_name = "" +# Usuario y contraseña (para mysql/postgres). +db_user = "" +db_pass = "" +# Servidor (para mysql/postgres). db_host = "localhost" +# Puerto (para mysql/postgres), siendo 0 el puerto predeterminado (3306 ó 5432). db_port = 0 # Número máximo de conexiones habilitadas. max_pool_size = 5 diff --git a/pagetop/src/base/module/mod.rs b/pagetop/src/base/module/mod.rs index 3a4e2d51..ab1169b4 100644 --- a/pagetop/src/base/module/mod.rs +++ b/pagetop/src/base/module/mod.rs @@ -1,3 +1,5 @@ pub mod admin; pub mod homepage; + +#[cfg(any(feature = "mysql", feature = "postgres", feature = "sqlite"))] pub mod user; diff --git a/pagetop/src/config.rs b/pagetop/src/config.rs index c6232c7a..fedffc21 100644 --- a/pagetop/src/config.rs +++ b/pagetop/src/config.rs @@ -89,6 +89,7 @@ pub struct Log { #[derive(Debug, Deserialize)] pub struct Database { + pub db_type : String, pub db_name : String, pub db_user : String, pub db_pass : String, @@ -127,15 +128,16 @@ Ajustes globales y valores predeterminados para las secciones *\[app\]*, // [log] "log.tracing" => "Info", - "log.rolling" => "Daily", + "log.rolling" => "Stdout", "log.path" => "log", "log.prefix" => "tracing.log", - "log.format" => "json", + "log.format" => "Full", // [database] - "database.db_name" => "dbname", - "database.db_user" => "dbuser", - "database.db_pass" => "dbpass", + "database.db_type" => "", + "database.db_name" => "", + "database.db_user" => "", + "database.db_pass" => "", "database.db_host" => "localhost", "database.db_port" => 0, "database.max_pool_size" => 5, diff --git a/pagetop/src/core/global.rs b/pagetop/src/core/global.rs index 0879fe4c..0b307c56 100644 --- a/pagetop/src/core/global.rs +++ b/pagetop/src/core/global.rs @@ -1,4 +1,4 @@ -use crate::{Lazy, db}; +use crate::{Lazy, trace}; use crate::core::theme::Theme; use crate::core::module::Module; use crate::core::response::page::PageContainer; @@ -42,9 +42,13 @@ pub fn modules(cfg: &mut server::web::ServiceConfig) { } } -pub fn migrations(dbconn: &db::DbConn) { +#[cfg(any(feature = "mysql", feature = "postgres", feature = "sqlite"))] +pub fn check_migrations() { + trace::info!("Checking migrations."); for m in MODULES.read().unwrap().iter() { - m.migrations(dbconn).expect("Failed to run migrations"); + m.migrations( + &*server::db::DBCONN.read().unwrap() + ).expect("Failed to run migrations"); } } diff --git a/pagetop/src/core/module/definition.rs b/pagetop/src/core/module/definition.rs index e11efd2e..7dc6252b 100644 --- a/pagetop/src/core/module/definition.rs +++ b/pagetop/src/core/module/definition.rs @@ -1,6 +1,8 @@ -use crate::db; use crate::core::server; +#[cfg(any(feature = "mysql", feature = "postgres", feature = "sqlite"))] +use crate::db; + /// Los módulos deben implementar este *trait*. pub trait Module: Send + Sync { fn name(&self) -> &'static str; @@ -15,6 +17,7 @@ pub trait Module: Send + Sync { fn configure_module(&self, cfg: &mut server::web::ServiceConfig) { } + #[cfg(any(feature = "mysql", feature = "postgres", feature = "sqlite"))] #[allow(unused_variables)] fn migrations(&self, dbconn: &db::DbConn) -> Result<(), db::DbErr> { Ok(()) diff --git a/pagetop/src/core/server/app.rs b/pagetop/src/core/server/app.rs index b23b38f2..dd794a68 100644 --- a/pagetop/src/core/server/app.rs +++ b/pagetop/src/core/server/app.rs @@ -1,4 +1,4 @@ -use crate::{Lazy, base, db, locale, trace}; +use crate::{Lazy, base, trace}; use crate::config::SETTINGS; use crate::core::{Server, global, server}; use crate::core::theme::register_theme; @@ -13,7 +13,7 @@ pub struct Application { impl Application { pub async fn build(bootstrap: Option) -> Result { - // Imprime rótulo (opcional) de bienvenida. + // Imprime un rótulo de presentación (opcional). if SETTINGS.app.startup_banner.to_lowercase() != "off" { let figfont = figlet_rs::FIGfont::from_content( match SETTINGS.app.startup_banner.to_lowercase().as_str() { @@ -43,42 +43,11 @@ impl Application { Lazy::force(&server::tracing::TRACING); // Valida el identificador de idioma. - Lazy::force(&locale::LANGID); + Lazy::force(&server::locale::LANGID); - // Inicializa la conexión con la base de datos. - trace::info!( - "Connecting to database \"{}\" using a pool of {} connections.", - &SETTINGS.database.db_name, - &SETTINGS.database.max_pool_size - ); - - #[cfg(feature = "mysql")] - let db_type = "mysql"; - - #[cfg(feature = "postgres")] - let db_type = "postgres"; - - // https://github.com/launchbadge/sqlx/issues/1624 - let mut db_uri = db::DbUri::parse(format!( - "{}://{}/{}", - db_type, - &SETTINGS.database.db_host, - &SETTINGS.database.db_name - ).as_str()).unwrap(); - db_uri.set_username(&SETTINGS.database.db_user.as_str()).unwrap(); - db_uri.set_password(Some(&SETTINGS.database.db_pass.as_str())).unwrap(); - if SETTINGS.database.db_port != 0 { - db_uri.set_port(Some(SETTINGS.database.db_port)).unwrap(); - } - - let mut db_options = sea_orm::ConnectOptions::new(db_uri.to_string()); - db_options.max_connections(SETTINGS.database.max_pool_size); - - let dbconn = sea_orm::Database::connect::( - db_options.into() - ) - .await - .expect("Failed to connect to database"); + // Conecta con la base de datos (opcional). + #[cfg(any(feature = "mysql", feature = "postgres", feature = "sqlite"))] + Lazy::force(&server::db::DBCONN); // Registra los temas predefinidos. register_theme(&base::theme::aliner::AlinerTheme); @@ -87,6 +56,8 @@ impl Application { // Registra los módulos predeterminados. register_module(&base::module::admin::AdminModule); + // Registra los módulos que requieren base de datos. + #[cfg(any(feature = "mysql", feature = "postgres", feature = "sqlite"))] register_module(&base::module::user::UserModule); // Ejecuta la función de inicio de la aplicación. @@ -99,16 +70,15 @@ impl Application { // Al ser el último, puede sobrecargarse con la función de inicio. register_module(&base::module::homepage::HomepageModule); - // Run migrations. - trace::info!("Running migrations."); - global::migrations(&dbconn); + // Comprueba actualizaciones pendientes de la base de datos (opcional). + #[cfg(any(feature = "mysql", feature = "postgres", feature = "sqlite"))] + global::check_migrations(); // Prepara el servidor web. let server = server::HttpServer::new(move || { server::App::new() .wrap(tracing_actix_web::TracingLogger) .wrap(NormalizePath::new(TrailingSlash::Trim)) - .data(dbconn.clone()) .configure(&global::themes) .configure(&global::modules) }) diff --git a/pagetop/src/core/server/db.rs b/pagetop/src/core/server/db.rs new file mode 100644 index 00000000..8c5c9f1f --- /dev/null +++ b/pagetop/src/core/server/db.rs @@ -0,0 +1,60 @@ +use crate::{Lazy, db, run_now, trace}; +use crate::config::SETTINGS; + +use std::sync::RwLock; +use sea_orm::{ConnectOptions, Database}; +use tracing_unwrap::ResultExt; + +pub static DBCONN: Lazy> = Lazy::new(|| { + trace::info!( + "Connecting to database \"{}\" using a pool of {} connections.", + &SETTINGS.database.db_name, + &SETTINGS.database.max_pool_size + ); + + let db_uri = match SETTINGS.database.db_type.as_str() { + "mysql" | "postgres" => { + let mut tmp_uri = db::DbUri::parse(format!( + "{}://{}/{}", + &SETTINGS.database.db_type, + &SETTINGS.database.db_host, + &SETTINGS.database.db_name + ).as_str()).unwrap(); + tmp_uri.set_username( + &SETTINGS.database.db_user.as_str() + ).unwrap(); + // https://github.com/launchbadge/sqlx/issues/1624 + tmp_uri.set_password( + Some(&SETTINGS.database.db_pass.as_str()) + ).unwrap(); + if SETTINGS.database.db_port != 0 { + tmp_uri.set_port( + Some(SETTINGS.database.db_port) + ).unwrap(); + } + tmp_uri + }, + "sqlite" => db::DbUri::parse( + format!("{}://{}", + &SETTINGS.database.db_type, + &SETTINGS.database.db_name + ).as_str()).unwrap(), + _ => { + trace::error!( + "Unrecognized database type \"{}\".", + &SETTINGS.database.db_type + ); + db::DbUri::parse("").unwrap() + } + }; + + let db_conn = run_now( + Database::connect::({ + let mut db_opt = ConnectOptions::new(db_uri.to_string()); + db_opt.max_connections(SETTINGS.database.max_pool_size); + db_opt.into() + }) + ).expect_or_log("Failed to connect to database"); + + RwLock::new(db_conn) +}); diff --git a/pagetop/src/core/server/locale.rs b/pagetop/src/core/server/locale.rs new file mode 100644 index 00000000..8bc6fc10 --- /dev/null +++ b/pagetop/src/core/server/locale.rs @@ -0,0 +1,23 @@ +use crate::{Lazy, trace}; +use crate::config::SETTINGS; + +use unic_langid::LanguageIdentifier; + +/// Almacena el Identificador de Idioma Unicode ([Unicode Language Identifier] +/// (https://unicode.org/reports/tr35/tr35.html#Unicode_language_identifier)) de +/// la aplicación, obtenido de `SETTINGS.app.language`. +pub static LANGID: Lazy = Lazy::new(|| { + match SETTINGS.app.language.parse() { + Ok(language) => language, + Err(_) => { + trace::warn!( + "Failed to parse language \"{}\". {}. {}. {}.", + SETTINGS.app.language, + "Unrecognized Unicode Language Identifier", + "Using \"en-US\"", + "Check the settings file", + ); + "en-US".parse().unwrap() + } + } +}); diff --git a/pagetop/src/core/server/mod.rs b/pagetop/src/core/server/mod.rs index ef85b53c..394886b4 100644 --- a/pagetop/src/core/server/mod.rs +++ b/pagetop/src/core/server/mod.rs @@ -4,5 +4,9 @@ pub use actix_web::{ mod tracing; -mod app; -pub use app::Application; +pub mod locale; + +#[cfg(any(feature = "mysql", feature = "postgres", feature = "sqlite"))] +pub mod db; + +pub mod app; diff --git a/pagetop/src/db.rs b/pagetop/src/db.rs index fb3f789e..b487cc19 100644 --- a/pagetop/src/db.rs +++ b/pagetop/src/db.rs @@ -12,3 +12,14 @@ pub mod entity { pub mod migration { pub use sea_schema::migration::prelude::*; } + +#[macro_export] +macro_rules! db_migrations { + ( $DBCONN:ident ) => {{ + $crate::run_now({ + use $crate::db::migration::MigratorTrait; + + migration::Migrator::up($DBCONN, None) + }) + }}; +} diff --git a/pagetop/src/lib.rs b/pagetop/src/lib.rs index 8d75fb08..b8485f96 100644 --- a/pagetop/src/lib.rs +++ b/pagetop/src/lib.rs @@ -11,11 +11,14 @@ pub use futures::executor::block_on as run_now; 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. + +#[cfg(any(feature = "mysql", feature = "postgres", feature = "sqlite"))] pub mod db; // Acceso a la base de datos. + pub mod core; // Servidor web y APIs para Temas, Módulos y Respuestas web. 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; +pub use crate::core::server::app::Application; diff --git a/pagetop/src/locale.rs b/pagetop/src/locale.rs index 07cb0d8e..22a01ba6 100644 --- a/pagetop/src/locale.rs +++ b/pagetop/src/locale.rs @@ -2,35 +2,12 @@ pub use fluent_templates::{static_loader as static_locale, Loader as Locale}; pub use fluent_templates; pub use fluent_templates::fluent_bundle::FluentValue; -use crate::{Lazy, trace}; -use crate::config::SETTINGS; - -use unic_langid::LanguageIdentifier; - -/// Almacena el Identificador de Idioma Unicode ([Unicode Language Identifier] -/// (https://unicode.org/reports/tr35/tr35.html#Unicode_language_identifier)) de -/// la aplicación, obtenido de `SETTINGS.app.language`. -pub static LANGID: Lazy = Lazy::new(|| { - match SETTINGS.app.language.parse() { - Ok(language) => language, - Err(_) => { - trace::warn!( - "Failed to parse language \"{}\". {}. {}. {}.", - SETTINGS.app.language, - "Unicode Language Identifier not recognized", - "Using \"en-US\"", - "Check the settings file", - ); - "en-US".parse().unwrap() - } - } -}); - #[macro_export] /// Permite integrar fácilmente localización en temas, módulos y componentes. macro_rules! localize { ( $DEF_LANGID:literal, $locales:literal $(, $core_locales:literal)? ) => { use $crate::locale::*; + use $crate::core::server::locale::LANGID; static_locale! { static LOCALES = { diff --git a/pagetop/src/prelude.rs b/pagetop/src/prelude.rs index bcff745b..f0a4b031 100644 --- a/pagetop/src/prelude.rs +++ b/pagetop/src/prelude.rs @@ -1,15 +1,12 @@ //! Re-exporta recursos comunes. -pub use crate::{ - args, - db_migrations, -}; - +pub use crate::args; pub use crate::config::SETTINGS; pub use crate::trace; pub use crate::localize; -pub use crate::db; +#[cfg(any(feature = "mysql", feature = "postgres", feature = "sqlite"))] +pub use crate::{db, db_migrations}; pub use crate::core::theme::*; pub use crate::core::module::*; diff --git a/pagetop/src/util.rs b/pagetop/src/util.rs index 5150d517..c56d2146 100644 --- a/pagetop/src/util.rs +++ b/pagetop/src/util.rs @@ -18,17 +18,6 @@ macro_rules! args { }}; } -#[macro_export] -macro_rules! db_migrations { - ( $DBCONN:ident ) => {{ - $crate::run_now({ - use $crate::db::migration::MigratorTrait; - - migration::Migrator::up($DBCONN, None) - }) - }}; -} - pub fn valid_id(id: &str) -> Option { let id = id.trim().replace(" ", "_").to_lowercase(); match id.is_empty() {