diff --git a/Cargo.toml b/Cargo.toml index 7aa29cae..0c3a0a43 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,8 +25,11 @@ categories = [ [dependencies] doc-comment = "0.3.3" -once_cell = "1.9.0" -figlet-rs = "0.1.3" +downcast-rs = "1.2.0" +figlet-rs = "0.1.3" +futures = "0.3" +once_cell = "1.9.0" +url = "2.2.2" config_rs = { package = "config", version = "0.11.0", features = ["toml"] } @@ -44,26 +47,22 @@ actix-web-static-files = "3.0.5" maud = { version = "0.23.0", features = ["actix-web"] } sycamore = { version = "0.7.1", features = ["ssr"] } -downcast-rs = "1.2.0" -url = "2.2.2" - serde = { version = "1.0", features = ["derive"] } -[dependencies.sqlx] -version = "0.5.11" -features = ["migrate", "runtime-async-std-native-tls"] +[dependencies.sea-orm] +version = "0.6" +features = ["debug-print", "macros", "runtime-async-std-native-tls"] default-features = false -[dependencies.refinery] -version = "0.8.4" - -[dependencies.barrel] -version = "0.7.0" +[dependencies.sea-schema] +version = "0.5" +features = ["debug-print", "migration"] +default-features = false [features] default = ["mysql"] -mysql = ["sqlx/mysql", "refinery/mysql", "barrel/mysql"] -postgres = ["sqlx/postgres", "refinery/postgres", "barrel/pg"] +mysql = ["sea-orm/sqlx-mysql"] +postgres = ["sea-orm/sqlx-postgres"] [build-dependencies] actix-web-static-files = "3.0.5" diff --git a/src/base/module/admin/migrations/V1__create_table_system.rs b/src/base/module/admin/migrations/V1__create_table_system.rs deleted file mode 100644 index 2cd6196a..00000000 --- a/src/base/module/admin/migrations/V1__create_table_system.rs +++ /dev/null @@ -1,13 +0,0 @@ -use crate::prelude::*; - -pub fn migration() -> String { - let mut m = db::Migration::new(); - - m.create_table("system", |t| { - t.add_column("id", db::types::primary()); - t.add_column("title", db::types::varchar(255)); - t.add_column("is_completed", db::types::boolean().default(false)); - }); - - m.make::() -} diff --git a/src/base/module/admin/mod.rs b/src/base/module/admin/mod.rs index 52079841..0bb1202d 100644 --- a/src/base/module/admin/mod.rs +++ b/src/base/module/admin/mod.rs @@ -1,7 +1,6 @@ use crate::prelude::*; localize!("en-US", "src/base/module/admin/locales"); -embed_migrations!("src/base/module/admin/migrations"); mod summary; @@ -26,8 +25,4 @@ impl Module for AdminModule { .route("", server::web::get().to(summary::summary)) ); } - - fn configure_migrations(&self) -> Option { - Some(migrations::runner()) - } } diff --git a/src/base/module/user/entity/mod.rs b/src/base/module/user/entity/mod.rs new file mode 100644 index 00000000..22d12a38 --- /dev/null +++ b/src/base/module/user/entity/mod.rs @@ -0,0 +1 @@ +pub mod user; diff --git a/src/base/module/user/entity/user.rs b/src/base/module/user/entity/user.rs new file mode 100644 index 00000000..d0db4a98 --- /dev/null +++ b/src/base/module/user/entity/user.rs @@ -0,0 +1,18 @@ +use crate::db::entity::*; +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Deserialize, Serialize)] +#[sea_orm(table_name = "user")] +pub struct Model { + #[sea_orm(primary_key)] + #[serde(skip_deserializing)] + pub id: i32, + pub title: String, + #[sea_orm(column_type = "Text")] + pub text: String, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation {} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/src/base/module/user/migration/m20220312_000001_create_table_user.rs b/src/base/module/user/migration/m20220312_000001_create_table_user.rs new file mode 100644 index 00000000..729b72e0 --- /dev/null +++ b/src/base/module/user/migration/m20220312_000001_create_table_user.rs @@ -0,0 +1,54 @@ +use crate::db::migration::*; + +#[derive(Iden)] +enum User { + Table, + Id, + Title, + Text, +} + +pub struct Migration; + +impl MigrationName for Migration { + fn name(&self) -> &str { + "m20220312_000001_create_table_user" + } +} + +#[async_trait::async_trait] +impl MigrationTrait for Migration { + async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { + manager + .create_table( + Table::create() + .table(User::Table) + .if_not_exists() + .col(ColumnDef::new(User::Id) + .integer() + .not_null() + .auto_increment() + .primary_key(), + ) + .col(ColumnDef::new(User::Title) + .string() + .not_null() + ) + .col(ColumnDef::new(User::Text) + .string() + .not_null() + ) + .to_owned() + ) + .await + } + + async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { + manager + .drop_table(Table::drop() + .table(User::Table) + .to_owned() + ) + .await + } +} diff --git a/src/base/module/user/migration/mod.rs b/src/base/module/user/migration/mod.rs new file mode 100644 index 00000000..8b9167a6 --- /dev/null +++ b/src/base/module/user/migration/mod.rs @@ -0,0 +1,12 @@ +use crate::db::migration::*; + +pub mod m20220312_000001_create_table_user; + +pub struct Migrator; + +#[async_trait::async_trait] +impl MigratorTrait for Migrator { + fn migrations() -> Vec> { + vec![Box::new(m20220312_000001_create_table_user::Migration)] + } +} diff --git a/src/base/module/user/migrations/V1__create_table_user.rs b/src/base/module/user/migrations/V1__create_table_user.rs deleted file mode 100644 index 9bc4e823..00000000 --- a/src/base/module/user/migrations/V1__create_table_user.rs +++ /dev/null @@ -1,13 +0,0 @@ -use crate::prelude::*; - -pub fn migration() -> String { - let mut m = db::Migration::new(); - - m.create_table("user", |t| { - t.add_column("id", db::types::primary()); - t.add_column("title", db::types::varchar(255)); - t.add_column("is_completed", db::types::boolean().default(false)); - }); - - m.make::() -} diff --git a/src/base/module/user/mod.rs b/src/base/module/user/mod.rs index 48c69820..7dc3c043 100644 --- a/src/base/module/user/mod.rs +++ b/src/base/module/user/mod.rs @@ -1,7 +1,9 @@ use crate::prelude::*; localize!("en-US", "src/base/module/user/locales"); -embed_migrations!("src/base/module/user/migrations"); + +mod entity; +mod migration; pub struct UserModule; @@ -22,8 +24,8 @@ impl Module for UserModule { cfg.route("/user/login", server::web::get().to(login)); } - fn configure_migrations(&self) -> Option { - Some(migrations::runner()) + fn migrations(&self, dbconn: &db::DbConn) -> Result<(), db::DbErr> { + db_migrations!(dbconn) } } diff --git a/src/core/global.rs b/src/core/global.rs index 7798d06d..0879fe4c 100644 --- a/src/core/global.rs +++ b/src/core/global.rs @@ -42,15 +42,9 @@ pub fn modules(cfg: &mut server::web::ServiceConfig) { } } -pub fn migrations(db_uri: db::Uri) { - let mut conn = refinery::config::Config::try_from(db_uri).unwrap(); +pub fn migrations(dbconn: &db::DbConn) { for m in MODULES.read().unwrap().iter() { - match m.configure_migrations() { - Some(migrations) => { - migrations.run(&mut conn).expect("Failed to run migrations"); - }, - _ => {} - }; + m.migrations(dbconn).expect("Failed to run migrations"); } } diff --git a/src/core/module/definition.rs b/src/core/module/definition.rs index 3f1e1e3d..e11efd2e 100644 --- a/src/core/module/definition.rs +++ b/src/core/module/definition.rs @@ -15,7 +15,8 @@ pub trait Module: Send + Sync { fn configure_module(&self, cfg: &mut server::web::ServiceConfig) { } - fn configure_migrations(&self) -> Option { - None + #[allow(unused_variables)] + fn migrations(&self, dbconn: &db::DbConn) -> Result<(), db::DbErr> { + Ok(()) } } diff --git a/src/core/server/app.rs b/src/core/server/app.rs index d758d850..1f2ec7f3 100644 --- a/src/core/server/app.rs +++ b/src/core/server/app.rs @@ -5,19 +5,8 @@ use crate::core::theme::register_theme; use crate::core::module::register_module; use std::io::Error; -use std::sync::RwLock; use actix_web::middleware::normalize::{NormalizePath, TrailingSlash}; -#[cfg(feature = "mysql")] -use sqlx::mysql::MySqlPoolOptions as DbPoolOptions; - -#[cfg(feature = "postgres")] -use sqlx::postgres::PgPoolOptions as DbPoolOptions; - -static DBCONN: Lazy>> = Lazy::new(|| { - RwLock::new(None) -}); - pub struct Application { server: Server, } @@ -70,7 +59,7 @@ impl Application { let db_type = "postgres"; // https://github.com/launchbadge/sqlx/issues/1624 - let mut db_uri = db::Uri::parse(format!( + let mut db_uri = db::DbUri::parse(format!( "{}://{}/{}", db_type, &SETTINGS.database.db_host, @@ -82,14 +71,14 @@ impl Application { db_uri.set_port(Some(SETTINGS.database.db_port)).unwrap(); } - let db_pool = DbPoolOptions::new() - .max_connections(SETTINGS.database.max_pool_size) - .connect(db_uri.as_str()) - .await - .expect("Failed to connect to database"); + let mut db_options = sea_orm::ConnectOptions::new(db_uri.to_string()); + db_options.max_connections(SETTINGS.database.max_pool_size); - let mut dbconn = DBCONN.write().unwrap(); - *dbconn = Some(db_pool); + let dbconn = sea_orm::Database::connect::( + db_options.into() + ) + .await + .expect("Failed to connect to database"); // Registra los temas predefinidos. register_theme(&base::theme::aliner::AlinerTheme); @@ -112,13 +101,14 @@ impl Application { // Run migrations. trace::info!("Running migrations."); - global::migrations(db_uri); + global::migrations(&dbconn); // Prepara el servidor web. - let server = server::HttpServer::new(|| { + 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/src/db.rs b/src/db.rs index c754be9c..fb3f789e 100644 --- a/src/db.rs +++ b/src/db.rs @@ -1,17 +1,14 @@ -pub use url::Url as Uri; +pub use url::Url as DbUri; -#[cfg(feature = "mysql")] -pub use { - barrel::backend::MySql as Database, - sqlx::MySqlPool as Conn, +pub use sea_orm::{ + DbErr, + DatabaseConnection as DbConn, }; -#[cfg(feature = "postgres")] -pub use { - barrel::backend::Pg as Database, - sqlx::PgPool as Conn, -}; +pub mod entity { + pub use sea_orm::entity::prelude::*; +} -pub use barrel::{Migration, types}; -pub use refinery::embed_migrations; -pub use refinery::Runner as Migrations; +pub mod migration { + pub use sea_schema::migration::prelude::*; +} diff --git a/src/lib.rs b/src/lib.rs index a3906783..8d75fb08 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,6 +2,7 @@ pub use doc_comment::doc_comment; pub use once_cell::sync::Lazy; +pub use futures::executor::block_on as run_now; // ----------------------------------------------------------------------------- // APIs públicas. diff --git a/src/prelude.rs b/src/prelude.rs index f168e92f..bcff745b 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -1,14 +1,15 @@ //! Re-exporta recursos comunes. -pub use crate::args; -pub use crate::util; +pub use crate::{ + args, + db_migrations, +}; pub use crate::config::SETTINGS; pub use crate::trace; pub use crate::localize; pub use crate::db; -pub use crate::db::embed_migrations; pub use crate::core::theme::*; pub use crate::core::module::*; @@ -16,3 +17,5 @@ pub use crate::core::response::page::*; pub use crate::core::server; pub use crate::base::component::*; + +pub use crate::util; diff --git a/src/util.rs b/src/util.rs index 3fff0a23..5150d517 100644 --- a/src/util.rs +++ b/src/util.rs @@ -9,15 +9,26 @@ /// ]; /// ``` macro_rules! args { - ( $($key:expr => $value:expr),* ) => {{ + ( $($KEY:expr => $VALUE:expr),* ) => {{ let mut a = std::collections::HashMap::new(); $( - a.insert(String::from($key), $value.into()); + a.insert(String::from($KEY), $VALUE.into()); )* a }}; } +#[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() {