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