headless_lms_server/programs/seed/
mod.rs

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