Corrige ejecución de "migrations" al desinst. mód.

This commit is contained in:
Manuel Cillero 2022-07-31 18:48:08 +02:00
parent a09f3aba7b
commit 842dd5234b
22 changed files with 644 additions and 71 deletions

View file

@ -3,13 +3,19 @@ use pagetop::prelude::*;
struct Drust;
impl AppTrait for Drust {
fn enable_modules(&self) -> Vec<&'static dyn ModuleTrait> {
fn enable_modules(&self) -> Vec<ModuleStaticRef> {
vec![
&pagetop_admin::Admin,
&pagetop_user::User,
&pagetop_node::Node,
]
}
fn disable_modules(&self) -> Vec<ModuleStaticRef> {
vec![
// &pagetop_node::Node,
]
}
}
#[actix_web::main]

View file

@ -22,6 +22,7 @@ categories = [
]
[dependencies]
async-trait = "0.1.56"
concat-string = "1.0.1"
doc-comment = "0.3.3"
figlet-rs = "0.1.3"
@ -57,14 +58,13 @@ version = "0.9.1"
features = ["debug-print", "macros", "runtime-async-std-native-tls"]
default-features = false
optional = true
[dependencies.sea-orm-migration]
version = "0.9.1"
[dependencies.sea-schema]
version = "0.9.3"
optional = true
[features]
default = []
database = ["sea-orm", "sea-orm-migration"]
database = ["sea-orm", "sea-schema"]
mysql = ["database", "sea-orm/sqlx-mysql"]
postgres = ["database", "sea-orm/sqlx-postgres"]
sqlite = ["database", "sea-orm/sqlx-sqlite"]

View file

@ -27,6 +27,8 @@ impl Application {
#[cfg(feature = "database")]
LazyStatic::force(&super::db::DBCONN);
// Deshabilita los módulos indicados por la aplicación.
module::all::disable_modules(app.disable_modules());
// Habilita los módulos predeterminados.
module::all::enable_modules(vec![&base::module::homepage::DefaultHomePage]);
// Habilita los módulos de la aplicación.

View file

@ -1,18 +1,18 @@
use crate::core::module::ModuleTrait;
use crate::core::theme::ThemeTrait;
use crate::core::module::ModuleStaticRef;
use crate::core::theme::ThemeStaticRef;
pub trait AppTrait: Send + Sync {
fn bootstrap(&self) {}
fn enable_modules(&self) -> Vec<&'static dyn ModuleTrait> {
fn enable_modules(&self) -> Vec<ModuleStaticRef> {
vec![]
}
fn disable_modules(&self) -> Vec<&'static dyn ModuleTrait> {
fn disable_modules(&self) -> Vec<ModuleStaticRef> {
vec![]
}
fn themes(&self) -> Vec<&'static dyn ThemeTrait> {
fn themes(&self) -> Vec<ThemeStaticRef> {
vec![]
}
}

View file

@ -43,8 +43,8 @@ impl ComponentTrait for MenuItem {
fn default_render(&self, context: &mut PageContext) -> Markup {
match self.item_type() {
MenuItemType::Void => html! {
},
MenuItemType::Void => html! {},
MenuItemType::Label(label) => html! {
li class="label" { a href="#" { (label) } }
},

View file

@ -6,8 +6,6 @@ pub struct HtmlMarkup {
impl Default for HtmlMarkup {
fn default() -> Self {
HtmlMarkup {
markup: html! {},
}
HtmlMarkup { markup: html! {} }
}
}

View file

@ -1,4 +1,4 @@
mod definition;
pub use definition::{BaseModule, ModuleTrait};
pub use definition::{BaseModule, ModuleStaticRef, ModuleTrait};
pub(crate) mod all;

View file

@ -1,4 +1,4 @@
use super::ModuleTrait;
use super::ModuleStaticRef;
use crate::core::hook::add_action;
use crate::{app, trace, LazyStatic};
@ -7,29 +7,39 @@ use crate::{db::*, run_now};
use std::sync::RwLock;
// Enabled modules.
static ENABLED_MODULES: LazyStatic<RwLock<Vec<&dyn ModuleTrait>>> =
// DISABLED MODULES ********************************************************************************
static DISABLED_MODULES: LazyStatic<RwLock<Vec<ModuleStaticRef>>> =
LazyStatic::new(|| RwLock::new(Vec::new()));
/* Disabled modules.
static DISABLED_MODULES: Lazy<RwLock<Vec<&dyn ModuleTrait>>> = Lazy::new(|| {
RwLock::new(Vec::new())
}); */
pub fn enable_modules(modules: Vec<&'static dyn ModuleTrait>) {
for m in modules {
enable(m)
pub fn disable_modules(modules: Vec<ModuleStaticRef>) {
let mut disabled_modules = DISABLED_MODULES.write().unwrap();
for module in modules {
if !disabled_modules
.iter()
.any(|m| m.handler() == module.handler())
{
trace::debug!("Disabling the \"{}\" module", module.single_name());
disabled_modules.push(module);
}
}
}
fn enable(module: &'static dyn ModuleTrait) {
let mut list: Vec<&dyn ModuleTrait> = Vec::new();
add_to(&mut list, module);
// ENABLED MODULES *********************************************************************************
static ENABLED_MODULES: LazyStatic<RwLock<Vec<ModuleStaticRef>>> =
LazyStatic::new(|| RwLock::new(Vec::new()));
pub fn enable_modules(modules: Vec<ModuleStaticRef>) {
for module in modules {
let mut list: Vec<ModuleStaticRef> = Vec::new();
add_to_enabled(&mut list, module);
list.reverse();
ENABLED_MODULES.write().unwrap().append(&mut list);
}
}
fn add_to(list: &mut Vec<&dyn ModuleTrait>, module: &'static dyn ModuleTrait) {
fn add_to_enabled(list: &mut Vec<ModuleStaticRef>, module: ModuleStaticRef) {
if !ENABLED_MODULES
.read()
.unwrap()
@ -37,21 +47,31 @@ fn add_to(list: &mut Vec<&dyn ModuleTrait>, module: &'static dyn ModuleTrait) {
.any(|m| m.handler() == module.handler())
&& !list.iter().any(|m| m.handler() == module.handler())
{
trace::debug!("Enabling module \"{}\"", module.single_name());
if DISABLED_MODULES
.read()
.unwrap()
.iter()
.any(|m| m.handler() == module.handler())
{
panic!(
"Trying to enable \"{}\" module which is disabled",
module.single_name()
);
} else {
trace::debug!("Enabling the \"{}\" module", module.single_name());
list.push(module);
let mut dependencies = module.dependencies();
dependencies.reverse();
for d in dependencies.iter() {
add_to(list, *d);
add_to_enabled(list, *d);
}
}
}
/*
#[allow(unused_variables)]
pub fn disable_module(module: &'static dyn ModuleTrait) {
}
*/
// CONFIGURE MODULES *******************************************************************************
pub fn modules(cfg: &mut app::web::ServiceConfig) {
for m in ENABLED_MODULES.read().unwrap().iter() {
m.configure_service(cfg);
@ -68,6 +88,21 @@ pub fn register_actions() {
#[cfg(feature = "database")]
pub fn run_migrations() {
run_now({
struct Migrator;
impl MigratorTrait for Migrator {
fn migrations() -> Vec<MigrationItem> {
let mut migrations = vec![];
for m in DISABLED_MODULES.read().unwrap().iter() {
migrations.append(&mut m.migrations());
}
migrations
}
}
Migrator::down(&app::db::DBCONN, None)
})
.unwrap();
run_now({
struct Migrator;
impl MigratorTrait for Migrator {

View file

@ -5,6 +5,8 @@ use crate::util::{single_type_name, Handler};
#[cfg(feature = "database")]
use crate::db::MigrationItem;
pub type ModuleStaticRef = &'static dyn ModuleTrait;
pub trait BaseModule {
fn single_name(&self) -> &'static str;
}
@ -21,7 +23,7 @@ pub trait ModuleTrait: BaseModule + Send + Sync {
None
}
fn dependencies(&self) -> Vec<&'static dyn ModuleTrait> {
fn dependencies(&self) -> Vec<ModuleStaticRef> {
vec![]
}

View file

@ -1,4 +1,4 @@
mod definition;
pub use definition::{BaseTheme, ThemeTrait};
pub use definition::{BaseTheme, ThemeStaticRef, ThemeTrait};
pub(crate) mod all;

View file

@ -1,4 +1,4 @@
use super::ThemeTrait;
use super::ThemeStaticRef;
use crate::{app, theme_static_files, trace, LazyStatic};
use std::sync::RwLock;
@ -6,24 +6,23 @@ use std::sync::RwLock;
include!(concat!(env!("OUT_DIR"), "/theme.rs"));
// Temas registrados.
static THEMES: LazyStatic<RwLock<Vec<&dyn ThemeTrait>>> =
static THEMES: LazyStatic<RwLock<Vec<ThemeStaticRef>>> =
LazyStatic::new(|| RwLock::new(Vec::new()));
pub fn register_themes(themes: Vec<&'static dyn ThemeTrait>) {
for t in themes {
register(t)
}
}
fn register(theme: &'static dyn ThemeTrait) {
let mut themes = THEMES.write().unwrap();
if !themes.iter().any(|t| t.handler() == theme.handler()) {
pub fn register_themes(themes: Vec<ThemeStaticRef>) {
let mut registered_themes = THEMES.write().unwrap();
for theme in themes {
if !registered_themes
.iter()
.any(|t| t.handler() == theme.handler())
{
trace::debug!("Registering theme \"{}\"", theme.single_name());
themes.push(theme);
registered_themes.push(theme);
}
}
}
pub fn theme_by_single_name(single_name: &str) -> Option<&'static dyn ThemeTrait> {
pub fn theme_by_single_name(single_name: &str) -> Option<ThemeStaticRef> {
match THEMES
.write()
.unwrap()

View file

@ -7,6 +7,8 @@ use crate::html::{html, Favicon, Markup};
use crate::response::page::{Page, PageContext, PageOp};
use crate::util::{single_type_name, Handler};
pub type ThemeStaticRef = &'static dyn ThemeTrait;
pub trait BaseTheme {
fn single_name(&self) -> &'static str;
}

View file

@ -2,7 +2,18 @@ pub use url::Url as DbUri;
pub use sea_orm::{DatabaseConnection as DbConn, ExecResult, QueryResult};
pub use sea_orm_migration::prelude::*;
// El siguiente módulo migration es una versión simplificada del módulo sea_orm_migration (v0.9.1)
// https://github.com/SeaQL/sea-orm/tree/0.9.1/sea-orm-migration para evitar los errores generados
// por el paradigma modular de PageTop. Se copian los siguientes archivos del original:
//
// lib.rs => db/migration.rs (descartando el uso de algunos módulos y exportaciones)
// manager.rs => db/migration/manager.rs
// migrator.rs => db/migration/migrator.rs (suprimiendo la gestión de los errores)
// prelude.rs => db/migration/prelude.rs (evitando cli)
// seaql_migrations.rs => db/migration/seaql_migrations.rs
//
mod migration;
pub use migration::prelude::*;
pub type MigrationItem = Box<dyn MigrationTrait>;

View file

@ -0,0 +1,30 @@
//pub mod cli;
pub mod manager;
pub mod migrator;
pub mod prelude;
pub mod seaql_migrations;
//pub mod util;
pub use manager::*;
//pub use migrator::*;
//pub use async_trait;
//pub use sea_orm;
//pub use sea_orm::sea_query;
use sea_orm::DbErr;
pub trait MigrationName {
fn name(&self) -> &str;
}
/// The migration definition
#[async_trait::async_trait]
pub trait MigrationTrait: MigrationName + Send + Sync {
/// Define actions to perform when applying the migration
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr>;
/// Define actions to perform when rolling back the migration
async fn down(&self, _manager: &SchemaManager) -> Result<(), DbErr> {
Err(DbErr::Migration("We Don't Do That Here".to_owned()))
}
}

View file

@ -0,0 +1,133 @@
use sea_orm::sea_query::{
extension::postgres::{TypeAlterStatement, TypeCreateStatement, TypeDropStatement},
ForeignKeyCreateStatement, ForeignKeyDropStatement, IndexCreateStatement, IndexDropStatement,
TableAlterStatement, TableCreateStatement, TableDropStatement, TableRenameStatement,
TableTruncateStatement,
};
use sea_orm::{ConnectionTrait, DbBackend, DbConn, DbErr, StatementBuilder};
use sea_schema::{mysql::MySql, postgres::Postgres, probe::SchemaProbe, sqlite::Sqlite};
/// Helper struct for writing migration scripts in migration file
pub struct SchemaManager<'c> {
conn: &'c DbConn,
}
impl<'c> SchemaManager<'c> {
pub fn new(conn: &'c DbConn) -> Self {
Self { conn }
}
pub async fn exec_stmt<S>(&self, stmt: S) -> Result<(), DbErr>
where
S: StatementBuilder,
{
let builder = self.conn.get_database_backend();
self.conn.execute(builder.build(&stmt)).await.map(|_| ())
}
pub fn get_database_backend(&self) -> DbBackend {
self.conn.get_database_backend()
}
pub fn get_connection(&self) -> &'c DbConn {
self.conn
}
}
/// Schema Creation
impl<'c> SchemaManager<'c> {
pub async fn create_table(&self, stmt: TableCreateStatement) -> Result<(), DbErr> {
self.exec_stmt(stmt).await
}
pub async fn create_index(&self, stmt: IndexCreateStatement) -> Result<(), DbErr> {
self.exec_stmt(stmt).await
}
pub async fn create_foreign_key(&self, stmt: ForeignKeyCreateStatement) -> Result<(), DbErr> {
self.exec_stmt(stmt).await
}
pub async fn create_type(&self, stmt: TypeCreateStatement) -> Result<(), DbErr> {
self.exec_stmt(stmt).await
}
}
/// Schema Mutation
impl<'c> SchemaManager<'c> {
pub async fn alter_table(&self, stmt: TableAlterStatement) -> Result<(), DbErr> {
self.exec_stmt(stmt).await
}
pub async fn drop_table(&self, stmt: TableDropStatement) -> Result<(), DbErr> {
self.exec_stmt(stmt).await
}
pub async fn rename_table(&self, stmt: TableRenameStatement) -> Result<(), DbErr> {
self.exec_stmt(stmt).await
}
pub async fn truncate_table(&self, stmt: TableTruncateStatement) -> Result<(), DbErr> {
self.exec_stmt(stmt).await
}
pub async fn drop_index(&self, stmt: IndexDropStatement) -> Result<(), DbErr> {
self.exec_stmt(stmt).await
}
pub async fn drop_foreign_key(&self, stmt: ForeignKeyDropStatement) -> Result<(), DbErr> {
self.exec_stmt(stmt).await
}
pub async fn alter_type(&self, stmt: TypeAlterStatement) -> Result<(), DbErr> {
self.exec_stmt(stmt).await
}
pub async fn drop_type(&self, stmt: TypeDropStatement) -> Result<(), DbErr> {
self.exec_stmt(stmt).await
}
}
/// Schema Inspection
impl<'c> SchemaManager<'c> {
pub async fn has_table<T>(&self, table: T) -> Result<bool, DbErr>
where
T: AsRef<str>,
{
let stmt = match self.conn.get_database_backend() {
DbBackend::MySql => MySql::has_table(table),
DbBackend::Postgres => Postgres::has_table(table),
DbBackend::Sqlite => Sqlite::has_table(table),
};
let builder = self.conn.get_database_backend();
let res = self
.conn
.query_one(builder.build(&stmt))
.await?
.ok_or_else(|| DbErr::Custom("Failed to check table exists".to_owned()))?;
res.try_get("", "has_table")
}
pub async fn has_column<T, C>(&self, table: T, column: C) -> Result<bool, DbErr>
where
T: AsRef<str>,
C: AsRef<str>,
{
let stmt = match self.conn.get_database_backend() {
DbBackend::MySql => MySql::has_column(table, column),
DbBackend::Postgres => Postgres::has_column(table, column),
DbBackend::Sqlite => Sqlite::has_column(table, column),
};
let builder = self.conn.get_database_backend();
let res = self
.conn
.query_one(builder.build(&stmt))
.await?
.ok_or_else(|| DbErr::Custom("Failed to check column exists".to_owned()))?;
res.try_get("", "has_column")
}
}

View file

@ -0,0 +1,326 @@
use std::collections::HashSet;
use std::fmt::Display;
use std::time::SystemTime;
use tracing::info;
use sea_orm::sea_query::{Alias, Expr, ForeignKey, Query, SelectStatement, SimpleExpr, Table};
use sea_orm::{
ActiveModelTrait, ActiveValue, ColumnTrait, Condition, ConnectionTrait, DbBackend, DbConn,
DbErr, EntityTrait, QueryFilter, QueryOrder, Schema, Statement,
};
use sea_schema::{mysql::MySql, postgres::Postgres, probe::SchemaProbe, sqlite::Sqlite};
use super::{seaql_migrations, MigrationTrait, SchemaManager};
#[derive(Debug, PartialEq)]
/// Status of migration
pub enum MigrationStatus {
/// Not yet applied
Pending,
/// Applied
Applied,
}
pub struct Migration {
migration: Box<dyn MigrationTrait>,
status: MigrationStatus,
}
impl Display for MigrationStatus {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let status = match self {
MigrationStatus::Pending => "Pending",
MigrationStatus::Applied => "Applied",
};
write!(f, "{}", status)
}
}
/// Performing migrations on a database
#[async_trait::async_trait]
pub trait MigratorTrait: Send {
/// Vector of migrations in time sequence
fn migrations() -> Vec<Box<dyn MigrationTrait>>;
/// Get list of migrations wrapped in `Migration` struct
fn get_migration_files() -> Vec<Migration> {
Self::migrations()
.into_iter()
.map(|migration| Migration {
migration,
status: MigrationStatus::Pending,
})
.collect()
}
/// Get list of applied migrations from database
async fn get_migration_models(db: &DbConn) -> Result<Vec<seaql_migrations::Model>, DbErr> {
Self::install(db).await?;
seaql_migrations::Entity::find()
.order_by_asc(seaql_migrations::Column::Version)
.all(db)
.await
}
/// Get list of migrations with status
async fn get_migration_with_status(db: &DbConn) -> Result<Vec<Migration>, DbErr> {
Self::install(db).await?;
let mut migration_files = Self::get_migration_files();
let migration_models = Self::get_migration_models(db).await?;
let migration_in_db: HashSet<String> = migration_models
.into_iter()
.map(|model| model.version)
.collect();
let migration_in_fs: HashSet<String> = migration_files
.iter()
.map(|file| file.migration.name().to_string())
.collect();
let pending_migrations = &migration_in_fs - &migration_in_db;
for migration_file in migration_files.iter_mut() {
if !pending_migrations.contains(migration_file.migration.name()) {
migration_file.status = MigrationStatus::Applied;
}
}
/*
let missing_migrations_in_fs = &migration_in_db - &migration_in_fs;
let errors: Vec<String> = missing_migrations_in_fs
.iter()
.map(|missing_migration| {
format!("Migration file of version '{}' is missing, this migration has been applied but its file is missing", missing_migration)
}).collect();
if !errors.is_empty() {
Err(DbErr::Custom(errors.join("\n")))
} else { */
Ok(migration_files)
/* } */
}
/// Get list of pending migrations
async fn get_pending_migrations(db: &DbConn) -> Result<Vec<Migration>, DbErr> {
Self::install(db).await?;
Ok(Self::get_migration_with_status(db)
.await?
.into_iter()
.filter(|file| file.status == MigrationStatus::Pending)
.collect())
}
/// Get list of applied migrations
async fn get_applied_migrations(db: &DbConn) -> Result<Vec<Migration>, DbErr> {
Self::install(db).await?;
Ok(Self::get_migration_with_status(db)
.await?
.into_iter()
.filter(|file| file.status == MigrationStatus::Applied)
.collect())
}
/// Create migration table `seaql_migrations` in the database
async fn install(db: &DbConn) -> Result<(), DbErr> {
let builder = db.get_database_backend();
let schema = Schema::new(builder);
let mut stmt = schema.create_table_from_entity(seaql_migrations::Entity);
stmt.if_not_exists();
db.execute(builder.build(&stmt)).await.map(|_| ())
}
/// Drop all tables from the database, then reapply all migrations
async fn fresh(db: &DbConn) -> Result<(), DbErr> {
Self::install(db).await?;
let db_backend = db.get_database_backend();
// Temporarily disable the foreign key check
if db_backend == DbBackend::Sqlite {
info!("Disabling foreign key check");
db.execute(Statement::from_string(
db_backend,
"PRAGMA foreign_keys = OFF".to_owned(),
))
.await?;
info!("Foreign key check disabled");
}
// Drop all foreign keys
if db_backend == DbBackend::MySql {
info!("Dropping all foreign keys");
let mut stmt = Query::select();
stmt.columns([Alias::new("TABLE_NAME"), Alias::new("CONSTRAINT_NAME")])
.from((
Alias::new("information_schema"),
Alias::new("table_constraints"),
))
.cond_where(
Condition::all()
.add(
Expr::expr(get_current_schema(db)).equals(
Alias::new("table_constraints"),
Alias::new("table_schema"),
),
)
.add(Expr::expr(Expr::value("FOREIGN KEY")).equals(
Alias::new("table_constraints"),
Alias::new("constraint_type"),
)),
);
let rows = db.query_all(db_backend.build(&stmt)).await?;
for row in rows.into_iter() {
let constraint_name: String = row.try_get("", "CONSTRAINT_NAME")?;
let table_name: String = row.try_get("", "TABLE_NAME")?;
info!(
"Dropping foreign key '{}' from table '{}'",
constraint_name, table_name
);
let mut stmt = ForeignKey::drop();
stmt.table(Alias::new(table_name.as_str()))
.name(constraint_name.as_str());
db.execute(db_backend.build(&stmt)).await?;
info!("Foreign key '{}' has been dropped", constraint_name);
}
info!("All foreign keys dropped");
}
// Drop all tables
let stmt = query_tables(db);
let rows = db.query_all(db_backend.build(&stmt)).await?;
for row in rows.into_iter() {
let table_name: String = row.try_get("", "table_name")?;
info!("Dropping table '{}'", table_name);
let mut stmt = Table::drop();
stmt.table(Alias::new(table_name.as_str()))
.if_exists()
.cascade();
db.execute(db_backend.build(&stmt)).await?;
info!("Table '{}' has been dropped", table_name);
}
// Restore the foreign key check
if db_backend == DbBackend::Sqlite {
info!("Restoring foreign key check");
db.execute(Statement::from_string(
db_backend,
"PRAGMA foreign_keys = ON".to_owned(),
))
.await?;
info!("Foreign key check restored");
}
// Reapply all migrations
Self::up(db, None).await
}
/// Rollback all applied migrations, then reapply all migrations
async fn refresh(db: &DbConn) -> Result<(), DbErr> {
Self::down(db, None).await?;
Self::up(db, None).await
}
/// Rollback all applied migrations
async fn reset(db: &DbConn) -> Result<(), DbErr> {
Self::down(db, None).await
}
/// Check the status of all migrations
async fn status(db: &DbConn) -> Result<(), DbErr> {
Self::install(db).await?;
info!("Checking migration status");
for Migration { migration, status } in Self::get_migration_with_status(db).await? {
info!("Migration '{}'... {}", migration.name(), status);
}
Ok(())
}
/// Apply pending migrations
async fn up(db: &DbConn, mut steps: Option<u32>) -> Result<(), DbErr> {
Self::install(db).await?;
let manager = SchemaManager::new(db);
if let Some(steps) = steps {
info!("Applying {} pending migrations", steps);
} else {
info!("Applying all pending migrations");
}
let migrations = Self::get_pending_migrations(db).await?.into_iter();
if migrations.len() == 0 {
info!("No pending migrations");
}
for Migration { migration, .. } in migrations {
if let Some(steps) = steps.as_mut() {
if steps == &0 {
break;
}
*steps -= 1;
}
info!("Applying migration '{}'", migration.name());
migration.up(&manager).await?;
info!("Migration '{}' has been applied", migration.name());
let now = SystemTime::now()
.duration_since(SystemTime::UNIX_EPOCH)
.expect("SystemTime before UNIX EPOCH!");
seaql_migrations::ActiveModel {
version: ActiveValue::Set(migration.name().to_owned()),
applied_at: ActiveValue::Set(now.as_secs() as i64),
}
.insert(db)
.await?;
}
Ok(())
}
/// Rollback applied migrations
async fn down(db: &DbConn, mut steps: Option<u32>) -> Result<(), DbErr> {
Self::install(db).await?;
let manager = SchemaManager::new(db);
if let Some(steps) = steps {
info!("Rolling back {} applied migrations", steps);
} else {
info!("Rolling back all applied migrations");
}
let migrations = Self::get_applied_migrations(db).await?.into_iter().rev();
if migrations.len() == 0 {
info!("No applied migrations");
}
for Migration { migration, .. } in migrations {
if let Some(steps) = steps.as_mut() {
if steps == &0 {
break;
}
*steps -= 1;
}
info!("Rolling back migration '{}'", migration.name());
migration.down(&manager).await?;
info!("Migration '{}' has been rollbacked", migration.name());
seaql_migrations::Entity::delete_many()
.filter(seaql_migrations::Column::Version.eq(migration.name()))
.exec(db)
.await?;
}
Ok(())
}
}
pub(crate) fn query_tables(db: &DbConn) -> SelectStatement {
match db.get_database_backend() {
DbBackend::MySql => MySql::query_tables(),
DbBackend::Postgres => Postgres::query_tables(),
DbBackend::Sqlite => Sqlite::query_tables(),
}
}
pub(crate) fn get_current_schema(db: &DbConn) -> SimpleExpr {
match db.get_database_backend() {
DbBackend::MySql => MySql::get_current_schema(),
DbBackend::Postgres => Postgres::get_current_schema(),
DbBackend::Sqlite => unimplemented!(),
}
}

View file

@ -0,0 +1,10 @@
//pub use super::cli;
pub use super::manager::SchemaManager;
pub use super::migrator::MigratorTrait;
pub use super::{MigrationName, MigrationTrait};
pub use async_trait;
pub use sea_orm;
pub use sea_orm::sea_query;
pub use sea_orm::sea_query::*;
pub use sea_orm::DbErr;
pub use sea_orm::DeriveMigrationName;

View file

@ -0,0 +1,14 @@
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, DeriveEntityModel)]
#[sea_orm(table_name = "seaql_migrations")]
pub struct Model {
#[sea_orm(primary_key, auto_increment = false)]
pub version: String,
pub applied_at: i64,
}
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
pub enum Relation {}
impl ActiveModelBehavior for ActiveModel {}

View file

@ -31,17 +31,23 @@ impl Favicon {
}
pub fn with_theme_color(mut self, color: &str) -> Self {
self.0.push(html! { meta name="theme-color" content=(color); });
self.0.push(html! {
meta name="theme-color" content=(color);
});
self
}
pub fn with_ms_tile_color(mut self, color: &str) -> Self {
self.0.push(html! { meta name="msapplication-TileColor" content=(color); });
self.0.push(html! {
meta name="msapplication-TileColor" content=(color);
});
self
}
pub fn with_ms_tile_image(mut self, image: &str) -> Self {
self.0.push(html! { meta name="msapplication-TileImage" content=(image); });
self.0.push(html! {
meta name="msapplication-TileImage" content=(image);
});
self
}

View file

@ -1,11 +1,10 @@
use super::PageOp;
use crate::config::SETTINGS;
use crate::core::theme::all::theme_by_single_name;
use crate::core::theme::ThemeTrait;
use crate::core::theme::{all::theme_by_single_name, ThemeStaticRef};
use crate::html::{html, Assets, Favicon, IdentifierValue, JavaScript, Markup, ModeJS, StyleSheet};
use crate::{base, concat_string, util, LazyStatic};
static DEFAULT_THEME: LazyStatic<&dyn ThemeTrait> =
static DEFAULT_THEME: LazyStatic<ThemeStaticRef> =
LazyStatic::new(|| match theme_by_single_name(&SETTINGS.app.theme) {
Some(theme) => theme,
None => &base::theme::bootsier::Bootsier,
@ -13,7 +12,7 @@ static DEFAULT_THEME: LazyStatic<&dyn ThemeTrait> =
#[rustfmt::skip]
pub struct PageContext {
theme : &'static dyn ThemeTrait,
theme : ThemeStaticRef,
favicon : Option<Favicon>,
metadata : Vec<(&'static str, &'static str)>,
properties : Vec<(&'static str, &'static str)>,
@ -94,7 +93,7 @@ impl PageContext {
/// PageContext GETTERS.
pub(crate) fn theme(&mut self) -> &'static dyn ThemeTrait {
pub(crate) fn theme(&mut self) -> ThemeStaticRef {
self.theme
}

View file

@ -5,7 +5,7 @@ mod mdbook;
struct PageTopWebSite;
impl AppTrait for PageTopWebSite {
fn enable_modules(&self) -> Vec<&'static dyn ModuleTrait> {
fn enable_modules(&self) -> Vec<ModuleStaticRef> {
vec![&mdbook::MdBook]
}
}