diff --git a/Cargo.lock b/Cargo.lock index 298f5ee82..7dc4e5359 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3794,6 +3794,7 @@ dependencies = [ name = "lemmy_db_views_site" version = "1.0.0-alpha.5" dependencies = [ + "anyhow", "chrono", "diesel", "diesel-async", diff --git a/crates/db_views/site/Cargo.toml b/crates/db_views/site/Cargo.toml index 95367b908..6873a913f 100644 --- a/crates/db_views/site/Cargo.toml +++ b/crates/db_views/site/Cargo.toml @@ -25,6 +25,7 @@ full = [ "lemmy_db_views_api_misc/full", "lemmy_db_views_person/full", "lemmy_db_views_readable_federation_state/full", + "anyhow", ] ts-rs = ["dep:ts-rs"] @@ -42,3 +43,4 @@ serde = { workspace = true } serde_with = { workspace = true } ts-rs = { workspace = true, optional = true } url = { workspace = true } +anyhow = { workspace = true, optional = true } diff --git a/crates/db_views/site/src/impls.rs b/crates/db_views/site/src/impls.rs index 62b5df021..47f66121a 100644 --- a/crates/db_views/site/src/impls.rs +++ b/crates/db_views/site/src/impls.rs @@ -40,7 +40,7 @@ impl SiteView { Ok(local_site) }) .await - .map_err(|_e: Arc| LemmyErrorType::LocalSiteNotSetup.into()) + .map_err(|e: Arc| anyhow::anyhow!("err getting local site: {e:?}").into()) } } diff --git a/crates/routes/src/utils/setup_local_site.rs b/crates/routes/src/utils/setup_local_site.rs index b2f469529..5e891093a 100644 --- a/crates/routes/src/utils/setup_local_site.rs +++ b/crates/routes/src/utils/setup_local_site.rs @@ -1,5 +1,10 @@ use activitypub_federation::http_signatures::generate_actor_keypair; use chrono::Utc; +use diesel::{ + dsl::{exists, not, select}, + query_builder::AsQuery, +}; +use diesel_async::{scoped_futures::ScopedFutureExt, RunQueryDsl}; use lemmy_api_utils::utils::generate_inbox_url; use lemmy_db_schema::{ source::{ @@ -11,8 +16,9 @@ use lemmy_db_schema::{ site::{Site, SiteInsertForm}, }, traits::{ApubActor, Crud}, - utils::DbPool, + utils::{get_conn, DbPool}, }; +use lemmy_db_schema_file::schema::local_site; use lemmy_db_views_site::SiteView; use lemmy_utils::{ error::{LemmyErrorExt, LemmyErrorType, LemmyResult}, @@ -22,89 +28,100 @@ use tracing::info; use url::Url; pub async fn setup_local_site(pool: &mut DbPool<'_>, settings: &Settings) -> LemmyResult { - // Check to see if local_site exists - if let Ok(site_view) = SiteView::read_local(pool).await { - return Ok(site_view); + let conn = &mut get_conn(pool).await?; + // Check to see if local_site exists, without the cache wrapper + if select(not(exists(local_site::table.as_query()))) + .get_result(conn) + .await? + { + info!("No Local Site found, creating it."); + + let domain = settings + .get_hostname_without_port() + .with_lemmy_type(LemmyErrorType::Unknown("must have domain".into()))?; + + conn + .run_transaction(|conn| { + async move { + // Upsert this to the instance table + let instance = Instance::read_or_create(&mut conn.into(), domain).await?; + + if let Some(setup) = &settings.setup { + let person_keypair = generate_actor_keypair()?; + let person_ap_id = Person::generate_local_actor_url(&setup.admin_username, settings)?; + + // Register the user if there's a site setup + let person_form = PersonInsertForm { + ap_id: Some(person_ap_id.clone()), + inbox_url: Some(generate_inbox_url()?), + private_key: Some(person_keypair.private_key.into()), + ..PersonInsertForm::new( + setup.admin_username.clone(), + person_keypair.public_key, + instance.id, + ) + }; + let person_inserted = Person::create(&mut conn.into(), &person_form).await?; + + let local_user_form = LocalUserInsertForm { + email: setup.admin_email.clone(), + admin: Some(true), + ..LocalUserInsertForm::new(person_inserted.id, Some(setup.admin_password.clone())) + }; + LocalUser::create(&mut conn.into(), &local_user_form, vec![]).await?; + }; + + // Add an entry for the site table + let site_key_pair = generate_actor_keypair()?; + let site_ap_id = Url::parse(&settings.get_protocol_and_hostname())?; + + let name = settings + .setup + .clone() + .map(|s| s.site_name) + .unwrap_or_else(|| "New Site".to_string()); + let site_form = SiteInsertForm { + ap_id: Some(site_ap_id.clone().into()), + last_refreshed_at: Some(Utc::now()), + inbox_url: Some(generate_inbox_url()?), + private_key: Some(site_key_pair.private_key), + public_key: Some(site_key_pair.public_key), + ..SiteInsertForm::new(name, instance.id) + }; + let site = Site::create(&mut conn.into(), &site_form).await?; + + // Finally create the local_site row + let local_site_form = LocalSiteInsertForm { + site_setup: Some(settings.setup.is_some()), + ..LocalSiteInsertForm::new(site.id) + }; + let local_site = LocalSite::create(&mut conn.into(), &local_site_form).await?; + + // Create the rate limit table + let local_site_rate_limit_form = if cfg!(debug_assertions) { + LocalSiteRateLimitInsertForm { + message: Some(999), + post: Some(999), + register: Some(999), + image: Some(999), + comment: Some(999), + search: Some(999), + ..LocalSiteRateLimitInsertForm::new(local_site.id) + } + } else { + LocalSiteRateLimitInsertForm::new(local_site.id) + }; + // TODO these have to be set, because the database defaults are too low for the federation + // tests to pass, and there's no way to live update the rate limits without restarting the + // server. + // This can be removed once live rate limits are enabled. + LocalSiteRateLimit::create(&mut conn.into(), &local_site_rate_limit_form).await?; + Ok(()) + } + .scope_boxed() + }) + .await?; } - info!("No Local Site found, creating it."); - - let domain = settings - .get_hostname_without_port() - .with_lemmy_type(LemmyErrorType::Unknown("must have domain".into()))?; - - // Upsert this to the instance table - let instance = Instance::read_or_create(pool, domain).await?; - - if let Some(setup) = &settings.setup { - let person_keypair = generate_actor_keypair()?; - let person_ap_id = Person::generate_local_actor_url(&setup.admin_username, settings)?; - - // Register the user if there's a site setup - let person_form = PersonInsertForm { - ap_id: Some(person_ap_id.clone()), - inbox_url: Some(generate_inbox_url()?), - private_key: Some(person_keypair.private_key.into()), - ..PersonInsertForm::new( - setup.admin_username.clone(), - person_keypair.public_key, - instance.id, - ) - }; - let person_inserted = Person::create(pool, &person_form).await?; - - let local_user_form = LocalUserInsertForm { - email: setup.admin_email.clone(), - admin: Some(true), - ..LocalUserInsertForm::new(person_inserted.id, Some(setup.admin_password.clone())) - }; - LocalUser::create(pool, &local_user_form, vec![]).await?; - }; - - // Add an entry for the site table - let site_key_pair = generate_actor_keypair()?; - let site_ap_id = Url::parse(&settings.get_protocol_and_hostname())?; - - let name = settings - .setup - .clone() - .map(|s| s.site_name) - .unwrap_or_else(|| "New Site".to_string()); - let site_form = SiteInsertForm { - ap_id: Some(site_ap_id.clone().into()), - last_refreshed_at: Some(Utc::now()), - inbox_url: Some(generate_inbox_url()?), - private_key: Some(site_key_pair.private_key), - public_key: Some(site_key_pair.public_key), - ..SiteInsertForm::new(name, instance.id) - }; - let site = Site::create(pool, &site_form).await?; - - // Finally create the local_site row - let local_site_form = LocalSiteInsertForm { - site_setup: Some(settings.setup.is_some()), - ..LocalSiteInsertForm::new(site.id) - }; - let local_site = LocalSite::create(pool, &local_site_form).await?; - - // Create the rate limit table - let local_site_rate_limit_form = if cfg!(debug_assertions) { - LocalSiteRateLimitInsertForm { - message: Some(999), - post: Some(999), - register: Some(999), - image: Some(999), - comment: Some(999), - search: Some(999), - ..LocalSiteRateLimitInsertForm::new(local_site.id) - } - } else { - LocalSiteRateLimitInsertForm::new(local_site.id) - }; - // TODO these have to be set, because the database defaults are too low for the federation - // tests to pass, and there's no way to live update the rate limits without restarting the - // server. - // This can be removed once live rate limits are enabled. - LocalSiteRateLimit::create(pool, &local_site_rate_limit_form).await?; SiteView::read_local(pool).await }