headless_lms_server/programs/seed/
mod.rs

1pub mod builder;
2pub mod seed_certificate_fonts;
3pub mod seed_courses;
4pub mod seed_exercise_services;
5pub mod seed_file_storage;
6pub mod seed_helpers;
7pub mod seed_organizations;
8pub mod seed_playground_examples;
9pub mod seed_roles;
10mod seed_user_research_consents;
11pub mod seed_users;
12
13use std::{env, process::Command, sync::Arc, time::Duration};
14
15use crate::{domain::models_requests::JwtKey, setup_tracing};
16
17use anyhow::Context;
18use futures::try_join;
19
20use headless_lms_utils::futures::run_parallelly;
21use sqlx::{Pool, Postgres, migrate::MigrateDatabase, postgres::PgPoolOptions};
22use tracing::info;
23
24pub async fn main() -> anyhow::Result<()> {
25    let base_url = std::env::var("BASE_URL").context("BASE_URL must be defined")?;
26    let db_pool = setup_seed_environment().await?;
27    let jwt_key = Arc::new(JwtKey::try_from_env().expect("Failed to create JwtKey"));
28
29    // Initialize the global spec fetcher before any seeding
30    seed_helpers::init_seed_spec_fetcher(base_url.clone(), Arc::clone(&jwt_key))
31        .expect("Failed to initialize seed spec fetcher");
32
33    // Run parallelly to improve performance.
34    let (_, seed_users_result, _) = try_join!(
35        run_parallelly(seed_exercise_services::seed_exercise_services(
36            db_pool.clone()
37        )),
38        run_parallelly(seed_users::seed_users(db_pool.clone())),
39        run_parallelly(seed_playground_examples::seed_playground_examples(
40            db_pool.clone()
41        )),
42    )?;
43
44    // Not run parallely because waits another future that is not send.
45    let seed_file_storage_result = seed_file_storage::seed_file_storage().await?;
46
47    let (uh_cs_organization_result, _uh_mathstat_organization_id, _no_users_organization_id) = try_join!(
48        run_parallelly(seed_organizations::uh_cs::seed_organization_uh_cs(
49            db_pool.clone(),
50            seed_users_result,
51            base_url.clone(),
52            Arc::clone(&jwt_key),
53            seed_file_storage_result.clone()
54        )),
55        run_parallelly(
56            seed_organizations::uh_mathstat::seed_organization_uh_mathstat(
57                db_pool.clone(),
58                seed_users_result,
59                base_url.clone(),
60                Arc::clone(&jwt_key),
61                seed_file_storage_result.clone()
62            )
63        ),
64        run_parallelly(seed_organizations::no_users::seed_organization_no_users(
65            db_pool.clone()
66        ))
67    )?;
68
69    try_join!(
70        run_parallelly(seed_roles::seed_roles(
71            db_pool.clone(),
72            seed_users_result,
73            uh_cs_organization_result
74        )),
75        run_parallelly(seed_user_research_consents::seed_user_research_consents(
76            db_pool.clone(),
77            seed_users_result
78        )),
79        run_parallelly(seed_certificate_fonts::seed_certificate_fonts(
80            db_pool.clone()
81        ))
82    )?;
83
84    Ok(())
85}
86
87async fn setup_seed_environment() -> anyhow::Result<Pool<Postgres>> {
88    // TODO: Audit that the environment access only happens in single-threaded code.
89    unsafe { env::set_var("RUST_LOG", "info,sqlx=warn,headless_lms_models=info") };
90
91    dotenv::dotenv().ok();
92    setup_tracing()?;
93
94    let clean = env::args().any(|a| a == "clean");
95
96    let db_url = env::var("DATABASE_URL")?;
97    let db_pool = PgPoolOptions::new()
98        .max_connections(10)
99        .min_connections(5)
100        // the seed process can take a while, default is 30
101        .acquire_timeout(Duration::from_secs(90))
102        .connect(&db_url)
103        .await?;
104
105    if clean {
106        info!("cleaning");
107        // hardcoded for now
108        let status = Command::new("dropdb")
109            .args(["-U", "headless-lms"])
110            .args(["-h", "localhost"])
111            .args(["-p", "54328"])
112            .arg("--force")
113            .arg("-e")
114            .arg("headless_lms_dev")
115            .status()?;
116        assert!(status.success());
117        let db_url = env::var("DATABASE_URL")?;
118        Postgres::create_database(&db_url).await?;
119    }
120
121    if clean {
122        let mut conn = db_pool.acquire().await?;
123        info!("running migrations");
124        sqlx::migrate!("../migrations").run(&mut conn).await?;
125    }
126    Ok(db_pool)
127}