diff --git a/.dockerignore b/.dockerignore index 0c102f2..59dfed4 100644 --- a/.dockerignore +++ b/.dockerignore @@ -3,6 +3,7 @@ # Except: !/assets +!/migrations !/src !/style !/Cargo.lock diff --git a/Cargo.lock b/Cargo.lock index 2da0866..23c93d8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -675,7 +675,7 @@ dependencies = [ "nom", "pathdiff", "serde", - "toml", + "toml 0.5.11", ] [[package]] @@ -889,6 +889,17 @@ dependencies = [ "syn 2.0.48", ] +[[package]] +name = "diesel_migrations" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6036b3f0120c5961381b570ee20a02432d7e2d27ea60de9578799cf9156914ac" +dependencies = [ + "diesel", + "migrations_internals", + "migrations_macros", +] + [[package]] name = "diesel_table_macro_syntax" version = "0.1.0" @@ -1686,6 +1697,7 @@ dependencies = [ "cfg-if", "console_error_panic_hook", "diesel", + "diesel_migrations", "dotenv", "http", "lazy_static", @@ -1781,6 +1793,27 @@ version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" +[[package]] +name = "migrations_internals" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f23f71580015254b020e856feac3df5878c2c7a8812297edd6c0a485ac9dada" +dependencies = [ + "serde", + "toml 0.7.8", +] + +[[package]] +name = "migrations_macros" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cce3325ac70e67bbab5bd837a31cae01f1a6db64e0e744a33cb03a543469ef08" +dependencies = [ + "migrations_internals", + "proc-macro2", + "quote", +] + [[package]] name = "mime" version = "0.3.17" @@ -2468,6 +2501,15 @@ dependencies = [ "thiserror", ] +[[package]] +name = "serde_spanned" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1" +dependencies = [ + "serde", +] + [[package]] name = "serde_test" version = "1.0.176" @@ -2799,6 +2841,40 @@ dependencies = [ "serde", ] +[[package]] +name = "toml" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd79e69d3b627db300ff956027cc6c3798cef26d22526befdfcd12feeb6d2257" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.19.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + [[package]] name = "tower-service" version = "0.3.2" @@ -3157,6 +3233,15 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" +[[package]] +name = "winnow" +version = "0.5.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7cad8365489051ae9f054164e459304af2e7e9bb407c958076c8bf4aef52da5" +dependencies = [ + "memchr", +] + [[package]] name = "winreg" version = "0.50.0" diff --git a/Cargo.toml b/Cargo.toml index 59e9c73..8d1b2a6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,6 +29,7 @@ diesel = { version = "2.1.4", features = ["postgres", "r2d2"], optional = true } lazy_static = { version = "1.4.0", optional = true } serde = { versions = "1.0.195", features = ["derive"] } openssl = { version = "0.10.63", optional = true } +diesel_migrations = { version = "2.1.0", optional = true } actix-identity = { version = "0.7.0", optional = true } actix-session = { version = "0.9.0", features = ["redis-rs-session"], optional = true } pbkdf2 = { version = "0.12.2", features = ["simple"], optional = true } @@ -47,6 +48,7 @@ ssr = [ "diesel", "lazy_static", "openssl", + "diesel_migrations", "actix-identity", "actix-session", "pbkdf2", diff --git a/Dockerfile b/Dockerfile index 9292b01..c02cd2f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -34,6 +34,7 @@ RUN npx tailwindcss -i /app/style/main.scss -o /app/style/main.scss --minify COPY assets /app/assets COPY src /app/src +COPY migrations /app/migrations # Touch files to force rebuild RUN touch /app/src/main.rs && touch /app/src/lib.rs && touch /app/src/build.rs diff --git a/src/build.rs b/src/build.rs index 32eb98c..64a96cb 100644 --- a/src/build.rs +++ b/src/build.rs @@ -6,4 +6,6 @@ fn main() { "cargo:rustc-cfg=target=\"{}\"", std::env::var("TARGET").unwrap() ); + + println!("cargo:rerun-if-changed=migrations"); } diff --git a/src/database.rs b/src/database.rs index ca05c46..cb1bee1 100644 --- a/src/database.rs +++ b/src/database.rs @@ -13,6 +13,12 @@ use diesel::{ r2d2::Pool, }; +use diesel_migrations::{ + embed_migrations, + EmbeddedMigrations, + MigrationHarness, +}; + // See https://leward.eu/notes-on-diesel-a-rust-orm/ // Define some types to make it easier to work with Diesel @@ -95,5 +101,15 @@ pub fn get_db_conn() -> PgPooledConn { DB_POOL.get().expect("Failed to get a database connection from the pool.") } +/// Embedded database migrations into the binary +const DB_MIGRATIONS: EmbeddedMigrations = embed_migrations!(); + +/// Run any pending migrations in the database +/// Always safe to call, as it will only run migrations that have not already been run +pub fn migrate() { + let db_con = &mut get_db_conn(); + db_con.run_pending_migrations(DB_MIGRATIONS).expect("Could not run database migrations"); +} + } } diff --git a/src/main.rs b/src/main.rs index b16a7c2..f6a448f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,6 +8,9 @@ extern crate openssl; #[macro_use] extern crate diesel; +#[cfg(feature = "ssr")] +extern crate diesel_migrations; + #[cfg(feature = "ssr")] #[actix_web::main] async fn main() -> std::io::Result<()> { @@ -19,6 +22,9 @@ async fn main() -> std::io::Result<()> { use dotenv::dotenv; dotenv().ok(); + // Bring the database up to date + libretunes::database::migrate(); + let session_secret_key = if let Ok(key) = std::env::var("SESSION_SECRET_KEY") { Key::from(key.as_bytes()) } else {