Our Personal Data Server from scratch!
0

Configure Feed

Select the types of activity you want to include in your feed.

1use async_trait::async_trait; 2use chrono::{DateTime, Utc}; 3use serde::{Deserialize, Serialize}; 4use tranquil_types::{CidLink, Did, Handle}; 5use uuid::Uuid; 6 7use crate::DbError; 8use crate::invite_code::{InviteCodeError, ValidatedInviteCode}; 9 10#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] 11pub enum InviteCodeSortOrder { 12 #[default] 13 Recent, 14 Usage, 15} 16 17#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default, Serialize, Deserialize)] 18pub enum InviteCodeState { 19 #[default] 20 Active, 21 Disabled, 22} 23 24impl InviteCodeState { 25 pub fn is_active(self) -> bool { 26 matches!(self, Self::Active) 27 } 28 29 pub fn is_disabled(self) -> bool { 30 matches!(self, Self::Disabled) 31 } 32} 33 34impl From<bool> for InviteCodeState { 35 fn from(disabled: bool) -> Self { 36 if disabled { 37 Self::Disabled 38 } else { 39 Self::Active 40 } 41 } 42} 43 44impl From<Option<bool>> for InviteCodeState { 45 fn from(disabled: Option<bool>) -> Self { 46 Self::from(disabled.unwrap_or(false)) 47 } 48} 49 50impl From<InviteCodeState> for bool { 51 fn from(state: InviteCodeState) -> Self { 52 matches!(state, InviteCodeState::Disabled) 53 } 54} 55 56#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, sqlx::Type)] 57#[sqlx(type_name = "comms_channel", rename_all = "snake_case")] 58pub enum CommsChannel { 59 Email, 60 Discord, 61 Telegram, 62 Signal, 63} 64 65#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, sqlx::Type)] 66#[sqlx(type_name = "comms_type", rename_all = "snake_case")] 67pub enum CommsType { 68 Welcome, 69 EmailVerification, 70 PasswordReset, 71 EmailUpdate, 72 AccountDeletion, 73 AdminEmail, 74 PlcOperation, 75 TwoFactorCode, 76 PasskeyRecovery, 77 LegacyLoginAlert, 78 MigrationVerification, 79 ChannelVerification, 80} 81 82#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, sqlx::Type)] 83#[sqlx(type_name = "comms_status", rename_all = "snake_case")] 84pub enum CommsStatus { 85 Pending, 86 Processing, 87 Sent, 88 Failed, 89} 90 91#[derive(Debug, Clone, Serialize, Deserialize)] 92pub struct QueuedComms { 93 pub id: Uuid, 94 pub user_id: Option<Uuid>, 95 pub channel: CommsChannel, 96 pub comms_type: CommsType, 97 pub status: CommsStatus, 98 pub recipient: String, 99 pub subject: Option<String>, 100 pub body: String, 101 pub metadata: Option<serde_json::Value>, 102 pub attempts: i32, 103 pub max_attempts: i32, 104 pub last_error: Option<String>, 105 pub created_at: DateTime<Utc>, 106 pub updated_at: DateTime<Utc>, 107 pub scheduled_for: DateTime<Utc>, 108 pub processed_at: Option<DateTime<Utc>>, 109} 110 111#[derive(Debug, Clone, Serialize, Deserialize)] 112pub struct InviteCodeInfo { 113 pub code: String, 114 pub available_uses: i32, 115 pub state: InviteCodeState, 116 pub for_account: Option<Did>, 117 pub created_at: DateTime<Utc>, 118 pub created_by: Option<Did>, 119} 120 121#[derive(Debug, Clone, Serialize, Deserialize)] 122pub struct InviteCodeUse { 123 pub code: String, 124 pub used_by_did: Did, 125 pub used_by_handle: Option<Handle>, 126 pub used_at: DateTime<Utc>, 127} 128 129#[derive(Debug, Clone, Serialize, Deserialize)] 130pub struct InviteCodeRow { 131 pub code: String, 132 pub available_uses: i32, 133 pub disabled: Option<bool>, 134 pub created_by_user: Uuid, 135 pub created_at: DateTime<Utc>, 136} 137 138impl InviteCodeRow { 139 pub fn state(&self) -> InviteCodeState { 140 InviteCodeState::from(self.disabled) 141 } 142} 143 144#[derive(Debug, Clone)] 145pub struct ReservedSigningKey { 146 pub id: Uuid, 147 pub private_key_bytes: Vec<u8>, 148} 149 150#[derive(Debug, Clone)] 151pub struct DeletionRequest { 152 pub did: Did, 153 pub expires_at: DateTime<Utc>, 154} 155 156#[async_trait] 157pub trait InfraRepository: Send + Sync { 158 #[allow(clippy::too_many_arguments)] 159 async fn enqueue_comms( 160 &self, 161 user_id: Option<Uuid>, 162 channel: CommsChannel, 163 comms_type: CommsType, 164 recipient: &str, 165 subject: Option<&str>, 166 body: &str, 167 metadata: Option<serde_json::Value>, 168 ) -> Result<Uuid, DbError>; 169 170 async fn fetch_pending_comms( 171 &self, 172 now: DateTime<Utc>, 173 batch_size: i64, 174 ) -> Result<Vec<QueuedComms>, DbError>; 175 176 async fn mark_comms_sent(&self, id: Uuid) -> Result<(), DbError>; 177 178 async fn mark_comms_failed(&self, id: Uuid, error: &str) -> Result<(), DbError>; 179 180 async fn create_invite_code( 181 &self, 182 code: &str, 183 use_count: i32, 184 for_account: Option<&Did>, 185 ) -> Result<bool, DbError>; 186 187 async fn create_invite_codes_batch( 188 &self, 189 codes: &[String], 190 use_count: i32, 191 created_by_user: Uuid, 192 for_account: Option<&Did>, 193 ) -> Result<(), DbError>; 194 195 async fn get_invite_code_available_uses(&self, code: &str) -> Result<Option<i32>, DbError>; 196 197 async fn validate_invite_code<'a>( 198 &self, 199 code: &'a str, 200 ) -> Result<ValidatedInviteCode<'a>, InviteCodeError>; 201 202 async fn decrement_invite_code_uses( 203 &self, 204 code: &ValidatedInviteCode<'_>, 205 ) -> Result<(), DbError>; 206 207 async fn record_invite_code_use( 208 &self, 209 code: &ValidatedInviteCode<'_>, 210 used_by_user: Uuid, 211 ) -> Result<(), DbError>; 212 213 async fn get_invite_codes_for_account( 214 &self, 215 for_account: &Did, 216 ) -> Result<Vec<InviteCodeInfo>, DbError>; 217 218 async fn get_invite_code_uses(&self, code: &str) -> Result<Vec<InviteCodeUse>, DbError>; 219 220 async fn disable_invite_codes_by_code(&self, codes: &[String]) -> Result<(), DbError>; 221 222 async fn disable_invite_codes_by_account(&self, accounts: &[Did]) -> Result<(), DbError>; 223 224 async fn list_invite_codes( 225 &self, 226 cursor: Option<&str>, 227 limit: i64, 228 sort: InviteCodeSortOrder, 229 ) -> Result<Vec<InviteCodeRow>, DbError>; 230 231 async fn get_user_dids_by_ids(&self, user_ids: &[Uuid]) -> Result<Vec<(Uuid, Did)>, DbError>; 232 233 async fn get_invite_code_uses_batch( 234 &self, 235 codes: &[String], 236 ) -> Result<Vec<InviteCodeUse>, DbError>; 237 238 async fn get_invites_created_by_user( 239 &self, 240 user_id: Uuid, 241 ) -> Result<Vec<InviteCodeInfo>, DbError>; 242 243 async fn get_invite_code_info(&self, code: &str) -> Result<Option<InviteCodeInfo>, DbError>; 244 245 async fn get_invite_codes_by_users( 246 &self, 247 user_ids: &[Uuid], 248 ) -> Result<Vec<(Uuid, InviteCodeInfo)>, DbError>; 249 250 async fn get_invite_code_used_by_user(&self, user_id: Uuid) -> Result<Option<String>, DbError>; 251 252 async fn delete_invite_code_uses_by_user(&self, user_id: Uuid) -> Result<(), DbError>; 253 254 async fn delete_invite_codes_by_user(&self, user_id: Uuid) -> Result<(), DbError>; 255 256 async fn reserve_signing_key( 257 &self, 258 did: Option<&Did>, 259 public_key_did_key: &str, 260 private_key_bytes: &[u8], 261 expires_at: DateTime<Utc>, 262 ) -> Result<Uuid, DbError>; 263 264 async fn get_reserved_signing_key( 265 &self, 266 public_key_did_key: &str, 267 ) -> Result<Option<ReservedSigningKey>, DbError>; 268 269 async fn mark_signing_key_used(&self, key_id: Uuid) -> Result<(), DbError>; 270 271 async fn create_deletion_request( 272 &self, 273 token: &str, 274 did: &Did, 275 expires_at: DateTime<Utc>, 276 ) -> Result<(), DbError>; 277 278 async fn get_deletion_request(&self, token: &str) -> Result<Option<DeletionRequest>, DbError>; 279 280 async fn delete_deletion_request(&self, token: &str) -> Result<(), DbError>; 281 282 async fn delete_deletion_requests_by_did(&self, did: &Did) -> Result<(), DbError>; 283 284 async fn upsert_account_preference( 285 &self, 286 user_id: Uuid, 287 name: &str, 288 value_json: serde_json::Value, 289 ) -> Result<(), DbError>; 290 291 async fn insert_account_preference_if_not_exists( 292 &self, 293 user_id: Uuid, 294 name: &str, 295 value_json: serde_json::Value, 296 ) -> Result<(), DbError>; 297 298 async fn get_server_config(&self, key: &str) -> Result<Option<String>, DbError>; 299 300 async fn health_check(&self) -> Result<bool, DbError>; 301 302 async fn insert_report( 303 &self, 304 id: i64, 305 reason_type: &str, 306 reason: Option<&str>, 307 subject_json: serde_json::Value, 308 reported_by_did: &Did, 309 created_at: DateTime<Utc>, 310 ) -> Result<(), DbError>; 311 312 async fn delete_plc_tokens_for_user(&self, user_id: Uuid) -> Result<(), DbError>; 313 314 async fn insert_plc_token( 315 &self, 316 user_id: Uuid, 317 token: &str, 318 expires_at: DateTime<Utc>, 319 ) -> Result<(), DbError>; 320 321 async fn get_plc_token_expiry( 322 &self, 323 user_id: Uuid, 324 token: &str, 325 ) -> Result<Option<DateTime<Utc>>, DbError>; 326 327 async fn delete_plc_token(&self, user_id: Uuid, token: &str) -> Result<(), DbError>; 328 329 async fn get_account_preferences( 330 &self, 331 user_id: Uuid, 332 ) -> Result<Vec<(String, serde_json::Value)>, DbError>; 333 334 async fn replace_namespace_preferences( 335 &self, 336 user_id: Uuid, 337 namespace: &str, 338 preferences: Vec<(String, serde_json::Value)>, 339 ) -> Result<(), DbError>; 340 341 async fn get_notification_history( 342 &self, 343 user_id: Uuid, 344 limit: i64, 345 ) -> Result<Vec<NotificationHistoryRow>, DbError>; 346 347 async fn get_server_configs(&self, keys: &[&str]) -> Result<Vec<(String, String)>, DbError>; 348 349 async fn upsert_server_config(&self, key: &str, value: &str) -> Result<(), DbError>; 350 351 async fn delete_server_config(&self, key: &str) -> Result<(), DbError>; 352 353 async fn get_blob_storage_key_by_cid(&self, cid: &CidLink) -> Result<Option<String>, DbError>; 354 355 async fn delete_blob_by_cid(&self, cid: &CidLink) -> Result<(), DbError>; 356 357 async fn get_admin_account_info_by_did( 358 &self, 359 did: &Did, 360 ) -> Result<Option<AdminAccountInfo>, DbError>; 361 362 async fn get_admin_account_infos_by_dids( 363 &self, 364 dids: &[Did], 365 ) -> Result<Vec<AdminAccountInfo>, DbError>; 366 367 async fn get_invite_code_uses_by_users( 368 &self, 369 user_ids: &[Uuid], 370 ) -> Result<Vec<(Uuid, String)>, DbError>; 371} 372 373#[derive(Debug, Clone)] 374pub struct NotificationHistoryRow { 375 pub created_at: DateTime<Utc>, 376 pub channel: CommsChannel, 377 pub comms_type: CommsType, 378 pub status: CommsStatus, 379 pub subject: Option<String>, 380 pub body: String, 381} 382 383#[derive(Debug, Clone)] 384pub struct AdminAccountInfo { 385 pub id: Uuid, 386 pub did: Did, 387 pub handle: Handle, 388 pub email: Option<String>, 389 pub created_at: DateTime<Utc>, 390 pub invites_disabled: bool, 391 pub email_verified: bool, 392 pub deactivated_at: Option<DateTime<Utc>>, 393}