Alternative ATProto PDS implementation
at oauth 153 lines 6.6 kB view raw
1//! Preference handling for actor store. 2//! Based on https://github.com/blacksky-algorithms/rsky/blob/main/rsky-pds/src/actor_store/preference/mod.rs 3//! blacksky-algorithms/rsky is licensed under the Apache License 2.0 4//! 5//! Modified for SQLite backend 6 7use crate::models::actor_store::AccountPref; 8use anyhow::{Result, bail}; 9use diesel::*; 10use rsky_lexicon::app::bsky::actor::RefPreferences; 11use rsky_pds::actor_store::preference::pref_match_namespace; 12use rsky_pds::actor_store::preference::util::pref_in_scope; 13use rsky_pds::auth_verifier::AuthScope; 14 15pub struct PreferenceReader { 16 pub did: String, 17 pub db: deadpool_diesel::Pool< 18 deadpool_diesel::Manager<SqliteConnection>, 19 deadpool_diesel::sqlite::Object, 20 >, 21} 22 23impl PreferenceReader { 24 pub const fn new( 25 did: String, 26 db: deadpool_diesel::Pool< 27 deadpool_diesel::Manager<SqliteConnection>, 28 deadpool_diesel::sqlite::Object, 29 >, 30 ) -> Self { 31 Self { did, db } 32 } 33 34 pub async fn get_preferences( 35 &self, 36 namespace: Option<String>, 37 scope: AuthScope, 38 ) -> Result<Vec<RefPreferences>> { 39 use crate::schema::actor_store::account_pref::dsl as AccountPrefSchema; 40 41 let did = self.did.clone(); 42 self.db 43 .get() 44 .await? 45 .interact(move |conn| { 46 let prefs_res = AccountPrefSchema::account_pref 47 .filter(AccountPrefSchema::did.eq(&did)) 48 .select(AccountPref::as_select()) 49 .order(AccountPrefSchema::id.asc()) 50 .load(conn)?; 51 let account_prefs = prefs_res 52 .into_iter() 53 .filter(|pref| { 54 namespace 55 .as_ref() 56 .is_none_or(|namespace| pref_match_namespace(namespace, &pref.name)) 57 }) 58 .filter(|pref| pref_in_scope(scope.clone(), pref.name.clone())) 59 .map(|pref| { 60 let value_json_res = match pref.value_json { 61 None => bail!("preferences json null for {}", pref.name), 62 Some(value_json) => serde_json::from_str::<RefPreferences>(&value_json), 63 }; 64 match value_json_res { 65 Err(error) => bail!(error.to_string()), 66 Ok(value_json) => Ok(value_json), 67 } 68 }) 69 .collect::<Result<Vec<RefPreferences>>>()?; 70 Ok(account_prefs) 71 }) 72 .await 73 .expect("Failed to get preferences") 74 } 75 76 #[tracing::instrument(skip_all)] 77 pub async fn put_preferences( 78 &self, 79 values: Vec<RefPreferences>, 80 namespace: String, 81 scope: AuthScope, 82 ) -> Result<()> { 83 let did = self.did.clone(); 84 self.db.get().await? 85 .interact(move |conn| { 86 match values 87 .iter() 88 .all(|value| pref_match_namespace(&namespace, &value.get_type())) 89 { 90 false => bail!("Some preferences are not in the {namespace} namespace"), 91 true => { 92 if values 93 .iter().any(|value| !pref_in_scope(scope.clone(), value.get_type())) { 94 tracing::info!( 95 "@LOG: PreferenceReader::put_preferences() debug scope: {:?}, values: {:?}", 96 scope, 97 values 98 ); 99 bail!("Do not have authorization to set preferences."); 100 } 101 // get all current prefs for user and prep new pref rows 102 use crate::schema::actor_store::account_pref::dsl as AccountPrefSchema; 103 let all_prefs = AccountPrefSchema::account_pref 104 .filter(AccountPrefSchema::did.eq(&did)) 105 .select(AccountPref::as_select()) 106 .load(conn)?; 107 let put_prefs = values 108 .into_iter() 109 .map(|value| { 110 Ok(AccountPref { 111 id: 0, 112 name: value.get_type(), 113 value_json: Some(serde_json::to_string(&value)?), 114 }) 115 }) 116 .collect::<Result<Vec<AccountPref>>>()?; 117 118 let all_pref_ids_in_namespace = all_prefs 119 .iter() 120 .filter(|pref| pref_match_namespace(&namespace, &pref.name)) 121 .filter(|pref| pref_in_scope(scope.clone(), pref.name.clone())) 122 .map(|pref| pref.id) 123 .collect::<Vec<i32>>(); 124 // replace all prefs in given namespace 125 if !all_pref_ids_in_namespace.is_empty() { 126 _ = delete(AccountPrefSchema::account_pref) 127 .filter(AccountPrefSchema::id.eq_any(all_pref_ids_in_namespace)) 128 .execute(conn)?; 129 } 130 if !put_prefs.is_empty() { 131 _ = insert_into(AccountPrefSchema::account_pref) 132 .values( 133 put_prefs 134 .into_iter() 135 .map(|pref| { 136 ( 137 AccountPrefSchema::did.eq(&did), 138 AccountPrefSchema::name.eq(pref.name), 139 AccountPrefSchema::valueJson.eq(pref.value_json), 140 ) 141 }) 142 .collect::<Vec<_>>(), 143 ) 144 .execute(conn)?; 145 } 146 Ok(()) 147 } 148 } 149 }) 150 .await 151 .expect("Failed to put preferences") 152 } 153}