]> Untitled Git - lemmy.git/blob - crates/db_schema/src/impls/instance.rs
Omit local instance from federated instances list (#3712)
[lemmy.git] / crates / db_schema / src / impls / instance.rs
1 use crate::{
2   diesel::dsl::IntervalDsl,
3   newtypes::InstanceId,
4   schema::{federation_allowlist, federation_blocklist, instance, local_site, site},
5   source::instance::{Instance, InstanceForm},
6   utils::{get_conn, naive_now, DbPool},
7 };
8 use diesel::{
9   dsl::{insert_into, now},
10   result::Error,
11   sql_types::{Nullable, Timestamp},
12   ExpressionMethods,
13   QueryDsl,
14 };
15 use diesel_async::RunQueryDsl;
16
17 impl Instance {
18   /// Attempt to read Instance column for the given domain. If it doesnt exist, insert a new one.
19   /// There is no need for update as the domain of an existing instance cant change.
20   pub async fn read_or_create(pool: &mut DbPool<'_>, domain_: String) -> Result<Self, Error> {
21     use crate::schema::instance::domain;
22     let conn = &mut get_conn(pool).await?;
23
24     // First try to read the instance row and return directly if found
25     let instance = instance::table
26       .filter(domain.eq(&domain_))
27       .first::<Self>(conn)
28       .await;
29     match instance {
30       Ok(i) => Ok(i),
31       Err(diesel::NotFound) => {
32         // Instance not in database yet, insert it
33         let form = InstanceForm::builder()
34           .domain(domain_)
35           .updated(Some(naive_now()))
36           .build();
37         insert_into(instance::table)
38           .values(&form)
39           // Necessary because this method may be called concurrently for the same domain. This
40           // could be handled with a transaction, but nested transactions arent allowed
41           .on_conflict(instance::domain)
42           .do_update()
43           .set(&form)
44           .get_result::<Self>(conn)
45           .await
46       }
47       e => e,
48     }
49   }
50   pub async fn delete(pool: &mut DbPool<'_>, instance_id: InstanceId) -> Result<usize, Error> {
51     let conn = &mut get_conn(pool).await?;
52     diesel::delete(instance::table.find(instance_id))
53       .execute(conn)
54       .await
55   }
56
57   pub async fn read_all(pool: &mut DbPool<'_>) -> Result<Vec<Instance>, Error> {
58     let conn = &mut get_conn(pool).await?;
59     instance::table
60       .select(instance::all_columns)
61       .get_results(conn)
62       .await
63   }
64
65   pub async fn dead_instances(pool: &mut DbPool<'_>) -> Result<Vec<String>, Error> {
66     let conn = &mut get_conn(pool).await?;
67     instance::table
68       .select(instance::domain)
69       .filter(coalesce(instance::updated, instance::published).lt(now - 3.days()))
70       .get_results(conn)
71       .await
72   }
73
74   #[cfg(test)]
75   pub async fn delete_all(pool: &mut DbPool<'_>) -> Result<usize, Error> {
76     let conn = &mut get_conn(pool).await?;
77     diesel::delete(instance::table).execute(conn).await
78   }
79   pub async fn allowlist(pool: &mut DbPool<'_>) -> Result<Vec<Self>, Error> {
80     let conn = &mut get_conn(pool).await?;
81     instance::table
82       .inner_join(federation_allowlist::table)
83       .select(instance::all_columns)
84       .get_results(conn)
85       .await
86   }
87
88   pub async fn blocklist(pool: &mut DbPool<'_>) -> Result<Vec<Self>, Error> {
89     let conn = &mut get_conn(pool).await?;
90     instance::table
91       .inner_join(federation_blocklist::table)
92       .select(instance::all_columns)
93       .get_results(conn)
94       .await
95   }
96
97   pub async fn linked(pool: &mut DbPool<'_>) -> Result<Vec<Self>, Error> {
98     let conn = &mut get_conn(pool).await?;
99     instance::table
100       // omit instance representing the local site
101       .left_join(site::table.inner_join(local_site::table))
102       .filter(local_site::id.is_null())
103       // omit instances in the blocklist
104       .left_join(federation_blocklist::table)
105       .filter(federation_blocklist::id.is_null())
106       .select(instance::all_columns)
107       .get_results(conn)
108       .await
109   }
110 }
111
112 sql_function! { fn coalesce(x: Nullable<Timestamp>, y: Timestamp) -> Timestamp; }