diff --git a/pagetop-user/src/migration/m20220312_000001_create_table_role.rs b/pagetop-user/src/migration/m20220312_000001_create_table_role.rs index f1fa802c..d051b0f1 100644 --- a/pagetop-user/src/migration/m20220312_000001_create_table_role.rs +++ b/pagetop-user/src/migration/m20220312_000001_create_table_role.rs @@ -60,6 +60,7 @@ impl MigrationTrait for Migration { .values_panic(vec!["administrator".into(), "3".into()]), ) .await + .unwrap_or_error(|e| DbErr::Custom(e.message())) .map(|_| ()) } diff --git a/pagetop/src/core/module/all.rs b/pagetop/src/core/module/all.rs index 4e06deca..112126db 100644 --- a/pagetop/src/core/module/all.rs +++ b/pagetop/src/core/module/all.rs @@ -1,7 +1,8 @@ use crate::core::action::add_action; use crate::core::module::ModuleRef; use crate::core::theme::all::THEMES; -use crate::{service, trace, LazyStatic}; +use crate::locale::L10n; +use crate::{service, trace, LazyStatic, ResultExt}; #[cfg(feature = "database")] use crate::db::*; @@ -109,35 +110,37 @@ pub fn init_modules() { #[cfg(feature = "database")] pub fn run_migrations() { - run_now({ - struct Migrator; - impl MigratorTrait for Migrator { - fn migrations() -> Vec { - let mut migrations = vec![]; - for m in ENABLED_MODULES.read().unwrap().iter() { - migrations.append(&mut m.migrations()); + if let Some(dbconn) = &*DBCONN { + run_now({ + struct Migrator; + impl MigratorTrait for Migrator { + fn migrations() -> Vec { + let mut migrations = vec![]; + for m in ENABLED_MODULES.read().unwrap().iter() { + migrations.append(&mut m.migrations()); + } + migrations } - migrations } - } - Migrator::up(SchemaManagerConnection::Connection(&DBCONN), None) - }) - .unwrap(); + Migrator::up(SchemaManagerConnection::Connection(dbconn), None) + }) + .expect_or_log(L10n::l("db_migration_fail").message().as_str()); - run_now({ - struct Migrator; - impl MigratorTrait for Migrator { - fn migrations() -> Vec { - let mut migrations = vec![]; - for m in DROPPED_MODULES.read().unwrap().iter() { - migrations.append(&mut m.migrations()); + run_now({ + struct Migrator; + impl MigratorTrait for Migrator { + fn migrations() -> Vec { + let mut migrations = vec![]; + for m in DROPPED_MODULES.read().unwrap().iter() { + migrations.append(&mut m.migrations()); + } + migrations } - migrations } - } - Migrator::down(SchemaManagerConnection::Connection(&DBCONN), None) - }) - .unwrap(); + Migrator::down(SchemaManagerConnection::Connection(dbconn), None) + }) + .expect_or_log(L10n::l("db_migration_fail").message().as_str()); + } } // CONFIGURE SERVICES ****************************************************************************** diff --git a/pagetop/src/db.rs b/pagetop/src/db.rs index 1e5d7fcf..29e1f7fc 100644 --- a/pagetop/src/db.rs +++ b/pagetop/src/db.rs @@ -1,5 +1,7 @@ //! Acceso unificado y normalizado a base de datos. +use crate::locale::L10n; +use crate::result::{SafeResult, TraceErr}; use crate::{config, trace, LazyStatic, ResultExt}; pub use url::Url as DbUri; @@ -10,97 +12,140 @@ use sea_orm::{ConnectOptions, ConnectionTrait, Database, DatabaseBackend, Statem pub(crate) use futures::executor::block_on as run_now; -pub(crate) static DBCONN: LazyStatic = LazyStatic::new(|| { - trace::info!( - "Connecting to database \"{}\" using a pool of {} connections", - &config::SETTINGS.database.db_name, - &config::SETTINGS.database.max_pool_size - ); +pub(crate) static DBCONN: LazyStatic> = LazyStatic::new(|| { + if !config::SETTINGS.database.db_name.trim().is_empty() { + trace::info!( + "Connecting to database \"{}\" using a pool of {} connections", + &config::SETTINGS.database.db_name, + &config::SETTINGS.database.max_pool_size + ); - let db_uri = match config::SETTINGS.database.db_type.as_str() { - "mysql" | "postgres" => { - let mut tmp_uri = DbUri::parse( + let db_uri = match config::SETTINGS.database.db_type.as_str() { + "mysql" | "postgres" => { + let mut tmp_uri = DbUri::parse( + format!( + "{}://{}/{}", + &config::SETTINGS.database.db_type, + &config::SETTINGS.database.db_host, + &config::SETTINGS.database.db_name + ) + .as_str(), + ) + .unwrap(); + tmp_uri + .set_username(config::SETTINGS.database.db_user.as_str()) + .unwrap(); + // https://github.com/launchbadge/sqlx/issues/1624 + tmp_uri + .set_password(Some(config::SETTINGS.database.db_pass.as_str())) + .unwrap(); + if config::SETTINGS.database.db_port != 0 { + tmp_uri + .set_port(Some(config::SETTINGS.database.db_port)) + .unwrap(); + } + tmp_uri + } + "sqlite" => DbUri::parse( format!( - "{}://{}/{}", + "{}://{}", &config::SETTINGS.database.db_type, - &config::SETTINGS.database.db_host, &config::SETTINGS.database.db_name ) .as_str(), ) - .unwrap(); - tmp_uri - .set_username(config::SETTINGS.database.db_user.as_str()) - .unwrap(); - // https://github.com/launchbadge/sqlx/issues/1624 - tmp_uri - .set_password(Some(config::SETTINGS.database.db_pass.as_str())) - .unwrap(); - if config::SETTINGS.database.db_port != 0 { - tmp_uri - .set_port(Some(config::SETTINGS.database.db_port)) - .unwrap(); + .unwrap(), + _ => { + trace::error!( + "Unrecognized database type \"{}\"", + &config::SETTINGS.database.db_type + ); + DbUri::parse("").unwrap() } - tmp_uri - } - "sqlite" => DbUri::parse( - format!( - "{}://{}", - &config::SETTINGS.database.db_type, - &config::SETTINGS.database.db_name - ) - .as_str(), - ) - .unwrap(), - _ => { - trace::error!( - "Unrecognized database type \"{}\"", - &config::SETTINGS.database.db_type - ); - DbUri::parse("").unwrap() - } - }; + }; - run_now(Database::connect::({ - let mut db_opt = ConnectOptions::new(db_uri.to_string()); - db_opt.max_connections(config::SETTINGS.database.max_pool_size); - db_opt - })) - .expect_or_log("Failed to connect to database") + Some( + run_now(Database::connect::({ + let mut db_opt = ConnectOptions::new(db_uri.to_string()); + db_opt.max_connections(config::SETTINGS.database.max_pool_size); + db_opt + })) + .expect_or_log(L10n::l("db_connection_fail").message().as_str()), + ) + } else { + None + } }); -static DBBACKEND: LazyStatic = LazyStatic::new(|| DBCONN.get_database_backend()); - -pub async fn query(stmt: &mut Q) -> Result, DbErr> { - DBCONN - .query_all(Statement::from_string( - *DBBACKEND, - match *DBBACKEND { - DatabaseBackend::MySql => stmt.to_string(MysqlQueryBuilder), - DatabaseBackend::Postgres => stmt.to_string(PostgresQueryBuilder), - DatabaseBackend::Sqlite => stmt.to_string(SqliteQueryBuilder), - }, - )) - .await +pub async fn query(stmt: &mut Q) -> SafeResult>> { + match &*DBCONN { + Some(dbconn) => { + let dbbackend = dbconn.get_database_backend(); + match dbconn + .query_all(Statement::from_string( + dbbackend, + match dbbackend { + DatabaseBackend::MySql => stmt.to_string(MysqlQueryBuilder), + DatabaseBackend::Postgres => stmt.to_string(PostgresQueryBuilder), + DatabaseBackend::Sqlite => stmt.to_string(SqliteQueryBuilder), + }, + )) + .await + { + Ok(result) => SafeResult::Ok(Some(result)), + Err(e) => SafeResult::Err(TraceErr::error(L10n::n(e.to_string()), None)), + } + } + None => SafeResult::Err(TraceErr::trace( + L10n::l("db_connection_not_initialized"), + None, + )), + } } -pub async fn exec(stmt: &mut Q) -> Result, DbErr> { - DBCONN - .query_one(Statement::from_string( - *DBBACKEND, - match *DBBACKEND { - DatabaseBackend::MySql => stmt.to_string(MysqlQueryBuilder), - DatabaseBackend::Postgres => stmt.to_string(PostgresQueryBuilder), - DatabaseBackend::Sqlite => stmt.to_string(SqliteQueryBuilder), - }, - )) - .await +pub async fn exec(stmt: &mut Q) -> SafeResult> { + match &*DBCONN { + Some(dbconn) => { + let dbbackend = dbconn.get_database_backend(); + match dbconn + .query_one(Statement::from_string( + dbbackend, + match dbbackend { + DatabaseBackend::MySql => stmt.to_string(MysqlQueryBuilder), + DatabaseBackend::Postgres => stmt.to_string(PostgresQueryBuilder), + DatabaseBackend::Sqlite => stmt.to_string(SqliteQueryBuilder), + }, + )) + .await + { + Ok(result) => SafeResult::Ok(result), + Err(e) => SafeResult::Err(TraceErr::error(L10n::n(e.to_string()), None)), + } + } + None => SafeResult::Err(TraceErr::trace( + L10n::l("db_connection_not_initialized"), + None, + )), + } } -pub async fn exec_raw(stmt: String) -> Result { - DBCONN - .execute(Statement::from_string(*DBBACKEND, stmt)) - .await +pub async fn exec_raw(stmt: String) -> SafeResult> { + match &*DBCONN { + Some(dbconn) => { + let dbbackend = dbconn.get_database_backend(); + match dbconn + .execute(Statement::from_string(dbbackend, stmt)) + .await + { + Ok(result) => SafeResult::Ok(Some(result)), + Err(e) => SafeResult::Err(TraceErr::error(L10n::n(e.to_string()), None)), + } + } + None => SafeResult::Err(TraceErr::trace( + L10n::l("db_connection_not_initialized"), + None, + )), + } } // El siguiente módulo migration es una versión simplificada del módulo sea_orm_migration (v0.11.3) diff --git a/pagetop/src/locale/en-US/trace.ftl b/pagetop/src/locale/en-US/trace.ftl index 4e7a5abb..a5f93d39 100644 --- a/pagetop/src/locale/en-US/trace.ftl +++ b/pagetop/src/locale/en-US/trace.ftl @@ -2,3 +2,7 @@ # ERRORS. language_set_failure = Failed to set language. Unicode Language Identifier "{$language}" is not accepted. Using "en-US", check the settings file + +db_connection_fail = Failed to connect to database +db_connection_not_initialized = Database connection not initialized +db_migration_fail = Database update failed diff --git a/pagetop/src/locale/es-ES/trace.ftl b/pagetop/src/locale/es-ES/trace.ftl index 0b7ad6a7..18598a44 100644 --- a/pagetop/src/locale/es-ES/trace.ftl +++ b/pagetop/src/locale/es-ES/trace.ftl @@ -1,4 +1,8 @@ # DEBUG. # ERRORS. -language_set_failure = Error al establecer idioma. El Identificador de Lenguaje Unicode "{$language}" no es válido. Se usará "en-US". Comprobar archivo de configuración +language_set_failure = Fallo al asignar idioma. El Identificador de Lenguaje Unicode "{$language}" no es válido. Se usará "en-US". Comprobar archivo de configuración + +db_connection_fail = Fallo al conectar con la base de datos +db_connection_not_initialized = Conexión a la base de datos no inicializada +db_migration_fail = Fallo en la actualización de la base de datos diff --git a/pagetop/src/result.rs b/pagetop/src/result.rs index 9b57df8d..bcff5388 100644 --- a/pagetop/src/result.rs +++ b/pagetop/src/result.rs @@ -9,6 +9,24 @@ pub struct TraceErr { } impl TraceErr { + pub fn trace(trace: L10n, fallback: T) -> Self { + let message = trace.message(); + trace::trace!(message); + TraceErr { message, fallback } + } + + pub fn debug(trace: L10n, fallback: T) -> Self { + let message = trace.message(); + trace::debug!(message); + TraceErr { message, fallback } + } + + pub fn info(trace: L10n, fallback: T) -> Self { + let message = trace.message(); + trace::info!(message); + TraceErr { message, fallback } + } + pub fn warn(trace: L10n, fallback: T) -> Self { let message = trace.message(); trace::warn!(message); @@ -21,6 +39,8 @@ impl TraceErr { TraceErr { message, fallback } } + // TraceErr GETTERS. + pub fn message(self) -> String { self.message } @@ -36,10 +56,22 @@ pub enum SafeResult { } impl SafeResult { + #[inline] + pub fn unwrap_or_error(self, f: F) -> Result + where + F: FnOnce(TraceErr) -> E, + { + match self { + SafeResult::Ok(r) => Ok(r), + SafeResult::Err(e) => Err(f(e)), + } + } + + #[inline] pub fn unwrap_or_fallback(self) -> T { match self { - SafeResult::Ok(result) => result, - SafeResult::Err(trace) => trace.fallback(), + SafeResult::Ok(r) => r, + SafeResult::Err(e) => e.fallback(), } } } diff --git a/pagetop/tests/server/health_check.rs b/pagetop/tests/server/health_check.rs index db08e31e..eec9c11b 100644 --- a/pagetop/tests/server/health_check.rs +++ b/pagetop/tests/server/health_check.rs @@ -16,5 +16,5 @@ async fn health_check_works() { let req = service::test::TestRequest::get().uri("/").to_request(); let _resp = service::test::call_service(&app, req).await; -// assert_eq!("OK", "OK"); + // assert_eq!("OK", "OK"); }