docbox_database/migrations/
mod.rs1use crate::{
2 DbResult, DbTransaction,
3 models::{
4 tenant::Tenant,
5 tenant_migration::{CreateTenantMigration, TenantMigration},
6 },
7};
8use chrono::Utc;
9use std::ops::DerefMut;
10
11const TENANT_MIGRATIONS: &[(&str, &str)] = &[
12 (
13 "m1_create_users_table",
14 include_str!("./tenant/m1_create_users_table.sql"),
15 ),
16 (
17 "m2_create_document_box_table",
18 include_str!("./tenant/m2_create_document_box_table.sql"),
19 ),
20 (
21 "m3_create_folders_table",
22 include_str!("./tenant/m3_create_folders_table.sql"),
23 ),
24 (
25 "m4_create_files_table",
26 include_str!("./tenant/m4_create_files_table.sql"),
27 ),
28 (
29 "m5_create_generated_files_table",
30 include_str!("./tenant/m5_create_generated_files_table.sql"),
31 ),
32 (
33 "m6_create_links_table",
34 include_str!("./tenant/m6_create_links_table.sql"),
35 ),
36 (
37 "m7_create_edit_history_table",
38 include_str!("./tenant/m7_create_edit_history_table.sql"),
39 ),
40 (
41 "m8_create_tasks_table",
42 include_str!("./tenant/m8_create_tasks_table.sql"),
43 ),
44 (
45 "m9_create_presigned_upload_tasks_table",
46 include_str!("./tenant/m9_create_presigned_upload_tasks_table.sql"),
47 ),
48];
49
50pub async fn apply_tenant_migrations(
55 root_t: &mut DbTransaction<'_>,
56 t: &mut DbTransaction<'_>,
57 tenant: &Tenant,
58 target_migration_name: Option<&str>,
59) -> DbResult<()> {
60 let migrations =
61 TenantMigration::find_by_tenant(root_t.deref_mut(), tenant.id, &tenant.env).await?;
62
63 for (migration_name, migration) in TENANT_MIGRATIONS {
64 if target_migration_name
66 .is_some_and(|target_migration_name| target_migration_name.ne(*migration_name))
67 {
68 continue;
69 }
70
71 if migrations
73 .iter()
74 .any(|migration| migration.name.eq(migration_name))
75 {
76 continue;
77 }
78
79 apply_tenant_migration(t, migration_name, migration).await?;
81
82 TenantMigration::create(
84 root_t.deref_mut(),
85 CreateTenantMigration {
86 tenant_id: tenant.id,
87 env: tenant.env.clone(),
88 name: migration_name.to_string(),
89 applied_at: Utc::now(),
90 },
91 )
92 .await?;
93 }
94
95 Ok(())
96}
97pub async fn force_apply_tenant_migrations(
101 t: &mut DbTransaction<'_>,
102 target_migration_name: Option<&str>,
103) -> DbResult<()> {
104 for (migration_name, migration) in TENANT_MIGRATIONS {
105 if target_migration_name
107 .is_some_and(|target_migration_name| target_migration_name.ne(*migration_name))
108 {
109 continue;
110 }
111
112 apply_tenant_migration(t, migration_name, migration).await?;
113 }
114
115 Ok(())
116}
117
118async fn apply_tenant_migration(
120 db: &mut DbTransaction<'_>,
121 migration_name: &str,
122 migration: &str,
123) -> DbResult<()> {
124 let queries = migration
126 .split(';')
127 .map(|query| query.trim())
128 .filter(|query| !query.is_empty());
129
130 for query in queries {
131 let result = sqlx::query(query)
132 .execute(db.deref_mut())
133 .await
134 .inspect_err(|error| {
135 tracing::error!(?error, ?migration_name, "failed to perform migration")
136 })?;
137 let rows_affected = result.rows_affected();
138
139 tracing::debug!(?migration_name, ?rows_affected, "applied migration query");
140 }
141
142 Ok(())
143}