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::{AtUri, CidLink, Did, Handle, Nsid, Rkey, Tid}; 5use uuid::Uuid; 6 7use crate::DbError; 8use crate::backlink::Backlink; 9use crate::sequence::SequenceNumber; 10 11#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, sqlx::Type)] 12#[sqlx(type_name = "text", rename_all = "snake_case")] 13#[serde(rename_all = "snake_case")] 14pub enum RepoEventType { 15 Commit, 16 Identity, 17 Account, 18 Sync, 19} 20 21impl RepoEventType { 22 pub fn as_str(&self) -> &'static str { 23 match self { 24 Self::Commit => "commit", 25 Self::Identity => "identity", 26 Self::Account => "account", 27 Self::Sync => "sync", 28 } 29 } 30} 31 32#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, sqlx::Type)] 33#[sqlx(type_name = "text", rename_all = "lowercase")] 34#[serde(rename_all = "lowercase")] 35pub enum AccountStatus { 36 Active, 37 Takendown, 38 Suspended, 39 Deactivated, 40 Deleted, 41} 42 43impl AccountStatus { 44 pub fn as_str(&self) -> &'static str { 45 match self { 46 Self::Active => "active", 47 Self::Takendown => "takendown", 48 Self::Suspended => "suspended", 49 Self::Deactivated => "deactivated", 50 Self::Deleted => "deleted", 51 } 52 } 53 54 pub fn for_firehose(&self) -> Option<Self> { 55 match self { 56 Self::Active => None, 57 other => Some(*other), 58 } 59 } 60 61 pub fn parse(s: &str) -> Option<Self> { 62 match s.to_lowercase().as_str() { 63 "active" => Some(Self::Active), 64 "takendown" => Some(Self::Takendown), 65 "suspended" => Some(Self::Suspended), 66 "deactivated" => Some(Self::Deactivated), 67 "deleted" => Some(Self::Deleted), 68 _ => None, 69 } 70 } 71 72 pub fn is_active(&self) -> bool { 73 matches!(self, Self::Active) 74 } 75 76 pub fn is_takendown(&self) -> bool { 77 matches!(self, Self::Takendown) 78 } 79 80 pub fn is_deactivated(&self) -> bool { 81 matches!(self, Self::Deactivated) 82 } 83 84 pub fn is_suspended(&self) -> bool { 85 matches!(self, Self::Suspended) 86 } 87 88 pub fn is_deleted(&self) -> bool { 89 matches!(self, Self::Deleted) 90 } 91 92 pub fn allows_read(&self) -> bool { 93 matches!(self, Self::Active | Self::Deactivated) 94 } 95 96 pub fn allows_write(&self) -> bool { 97 matches!(self, Self::Active) 98 } 99 100 pub fn from_db_fields( 101 takedown_ref: Option<&str>, 102 deactivated_at: Option<DateTime<Utc>>, 103 ) -> Self { 104 if takedown_ref.is_some() { 105 Self::Takendown 106 } else if deactivated_at.is_some() { 107 Self::Deactivated 108 } else { 109 Self::Active 110 } 111 } 112} 113 114impl std::fmt::Display for AccountStatus { 115 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 116 f.write_str(self.as_str()) 117 } 118} 119 120#[derive(Debug, Clone, Serialize, Deserialize)] 121pub struct RepoAccountInfo { 122 pub user_id: Uuid, 123 pub did: Did, 124 pub deactivated_at: Option<DateTime<Utc>>, 125 pub takedown_ref: Option<String>, 126 pub repo_root_cid: Option<CidLink>, 127} 128 129#[derive(Debug, Clone, Serialize, Deserialize)] 130pub struct RepoInfo { 131 pub user_id: Uuid, 132 pub repo_root_cid: CidLink, 133 pub repo_rev: Option<Tid>, 134} 135 136#[derive(Debug, Clone, Serialize, Deserialize)] 137pub struct RecordInfo { 138 pub rkey: Rkey, 139 pub record_cid: CidLink, 140} 141 142#[derive(Debug, Clone, Serialize, Deserialize)] 143pub struct FullRecordInfo { 144 pub collection: Nsid, 145 pub rkey: Rkey, 146 pub record_cid: CidLink, 147} 148 149#[derive(Debug, Clone, Serialize, Deserialize)] 150pub struct RecordWithTakedown { 151 pub id: Uuid, 152 pub takedown_ref: Option<String>, 153} 154 155#[derive(Debug, Clone, Serialize, Deserialize)] 156pub struct RepoWithoutRev { 157 pub user_id: Uuid, 158 pub repo_root_cid: CidLink, 159} 160 161#[derive(Debug, Clone)] 162pub struct UserWithoutBlocks { 163 pub user_id: Uuid, 164 pub repo_root_cid: CidLink, 165 pub repo_rev: Option<Tid>, 166} 167 168#[derive(Debug, Clone)] 169pub struct UserNeedingRecordBlobsBackfill { 170 pub user_id: Uuid, 171 pub did: Did, 172} 173 174#[derive(Debug, Clone, Serialize, Deserialize)] 175pub struct RepoSeqEvent { 176 pub seq: SequenceNumber, 177} 178 179#[derive(Debug, Clone, Copy, PartialEq, Eq)] 180pub enum PruneCount { 181 Rows(u64), 182 Segments(u64), 183} 184 185impl PruneCount { 186 pub fn is_zero(&self) -> bool { 187 match self { 188 Self::Rows(n) | Self::Segments(n) => *n == 0, 189 } 190 } 191 192 pub fn count(&self) -> u64 { 193 match self { 194 Self::Rows(n) | Self::Segments(n) => *n, 195 } 196 } 197 198 pub fn unit(&self) -> &'static str { 199 match self { 200 Self::Rows(_) => "rows", 201 Self::Segments(_) => "segments", 202 } 203 } 204} 205 206impl std::fmt::Display for PruneCount { 207 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 208 write!(f, "{} {}", self.count(), self.unit()) 209 } 210} 211 212#[derive(Debug, Clone, Serialize, Deserialize)] 213pub struct EventBlockInline { 214 pub cid_bytes: Vec<u8>, 215 pub data: Vec<u8>, 216} 217 218#[derive(Debug, Clone, Serialize, Deserialize)] 219pub enum EventBlocks { 220 Inline(Vec<EventBlockInline>), 221 LegacyCids(Vec<CidLink>), 222} 223 224#[derive(Debug, Clone, Serialize, Deserialize)] 225pub struct SequencedEvent { 226 pub seq: SequenceNumber, 227 pub did: Did, 228 pub created_at: DateTime<Utc>, 229 pub event_type: RepoEventType, 230 pub commit_cid: Option<CidLink>, 231 pub prev_cid: Option<CidLink>, 232 pub prev_data_cid: Option<CidLink>, 233 pub ops: Option<serde_json::Value>, 234 pub blobs: Option<Vec<CidLink>>, 235 pub blocks: Option<EventBlocks>, 236 pub handle: Option<Handle>, 237 pub active: Option<bool>, 238 pub status: Option<AccountStatus>, 239 pub rev: Option<Tid>, 240} 241 242#[derive(Debug, Clone)] 243pub struct CommitEventData { 244 pub did: Did, 245 pub event_type: RepoEventType, 246 pub commit_cid: Option<CidLink>, 247 pub prev_cid: Option<CidLink>, 248 pub ops: Option<serde_json::Value>, 249 pub blobs: Option<Vec<CidLink>>, 250 pub blocks: Option<Vec<EventBlockInline>>, 251 pub prev_data_cid: Option<CidLink>, 252 pub rev: Option<Tid>, 253} 254 255#[derive(Debug, Clone, Serialize, Deserialize)] 256pub struct RepoListItem { 257 pub did: Did, 258 pub deactivated_at: Option<DateTime<Utc>>, 259 pub takedown_ref: Option<String>, 260 pub repo_root_cid: CidLink, 261 pub repo_rev: Option<Tid>, 262} 263 264#[derive(Debug, Clone)] 265pub struct ImportBlock { 266 pub cid_bytes: Vec<u8>, 267 pub data: Vec<u8>, 268} 269 270#[derive(Debug, Clone)] 271pub struct ImportRecord { 272 pub collection: Nsid, 273 pub rkey: Rkey, 274 pub record_cid: CidLink, 275} 276 277#[derive(Debug, Clone, PartialEq, Eq)] 278pub enum ImportRepoError { 279 RepoNotFound, 280 ConcurrentModification, 281 Database(String), 282} 283 284#[derive(Debug, Clone)] 285pub struct RecordUpsert { 286 pub collection: Nsid, 287 pub rkey: Rkey, 288 pub cid: CidLink, 289} 290 291#[derive(Debug, Clone)] 292pub struct RecordDelete { 293 pub collection: Nsid, 294 pub rkey: Rkey, 295} 296 297#[derive(Debug, Clone)] 298pub struct ApplyCommitInput { 299 pub user_id: Uuid, 300 pub did: Did, 301 pub expected_root_cid: Option<CidLink>, 302 pub new_root_cid: CidLink, 303 pub new_rev: Tid, 304 pub new_block_cids: Vec<Vec<u8>>, 305 pub obsolete_block_cids: Vec<Vec<u8>>, 306 pub record_upserts: Vec<RecordUpsert>, 307 pub record_deletes: Vec<RecordDelete>, 308 pub backlinks_to_add: Vec<Backlink>, 309 pub backlinks_to_remove: Vec<AtUri>, 310 pub commit_event: CommitEventData, 311} 312 313#[derive(Debug, Clone)] 314pub struct ApplyCommitResult { 315 pub is_account_active: bool, 316} 317 318#[derive(Debug, Clone, PartialEq, Eq)] 319pub enum ApplyCommitError { 320 RepoNotFound, 321 ConcurrentModification, 322 Database(String), 323} 324 325#[async_trait] 326pub trait RepoRepository: Send + Sync { 327 async fn create_repo( 328 &self, 329 user_id: Uuid, 330 did: &Did, 331 handle: &Handle, 332 repo_root_cid: &CidLink, 333 repo_rev: &Tid, 334 ) -> Result<(), DbError>; 335 336 async fn update_repo_root( 337 &self, 338 user_id: Uuid, 339 repo_root_cid: &CidLink, 340 repo_rev: &Tid, 341 ) -> Result<(), DbError>; 342 343 async fn update_repo_rev(&self, user_id: Uuid, repo_rev: &Tid) -> Result<(), DbError>; 344 345 async fn update_repo_status( 346 &self, 347 did: &Did, 348 takedown: Option<bool>, 349 takedown_ref: Option<&str>, 350 deactivated: Option<bool>, 351 ) -> Result<(), DbError>; 352 353 async fn delete_repo(&self, user_id: Uuid) -> Result<(), DbError>; 354 355 async fn get_repo_root_for_update(&self, user_id: Uuid) -> Result<Option<CidLink>, DbError>; 356 357 async fn get_repo(&self, user_id: Uuid) -> Result<Option<RepoInfo>, DbError>; 358 359 async fn get_repo_root_by_did(&self, did: &Did) -> Result<Option<CidLink>, DbError>; 360 361 async fn count_repos(&self) -> Result<i64, DbError>; 362 363 async fn get_repos_without_rev(&self) -> Result<Vec<RepoWithoutRev>, DbError>; 364 365 async fn upsert_records( 366 &self, 367 repo_id: Uuid, 368 collections: &[Nsid], 369 rkeys: &[Rkey], 370 record_cids: &[CidLink], 371 repo_rev: &Tid, 372 ) -> Result<(), DbError>; 373 374 async fn delete_records( 375 &self, 376 repo_id: Uuid, 377 collections: &[Nsid], 378 rkeys: &[Rkey], 379 ) -> Result<(), DbError>; 380 381 async fn delete_all_records(&self, repo_id: Uuid) -> Result<(), DbError>; 382 383 async fn get_record_cid( 384 &self, 385 repo_id: Uuid, 386 collection: &Nsid, 387 rkey: &Rkey, 388 ) -> Result<Option<CidLink>, DbError>; 389 390 #[allow(clippy::too_many_arguments)] 391 async fn list_records( 392 &self, 393 repo_id: Uuid, 394 collection: &Nsid, 395 cursor: Option<&Rkey>, 396 limit: i64, 397 reverse: bool, 398 rkey_start: Option<&Rkey>, 399 rkey_end: Option<&Rkey>, 400 ) -> Result<Vec<RecordInfo>, DbError>; 401 402 async fn get_all_records(&self, repo_id: Uuid) -> Result<Vec<FullRecordInfo>, DbError>; 403 404 async fn list_collections(&self, repo_id: Uuid) -> Result<Vec<Nsid>, DbError>; 405 406 async fn count_records(&self, repo_id: Uuid) -> Result<i64, DbError>; 407 408 async fn count_all_records(&self) -> Result<i64, DbError>; 409 410 async fn get_record_by_cid(&self, cid: &CidLink) 411 -> Result<Option<RecordWithTakedown>, DbError>; 412 413 async fn referenced_record_cids( 414 &self, 415 repo_id: Uuid, 416 cids: &[CidLink], 417 excluded_keys: &[(&Nsid, &Rkey)], 418 ) -> Result<Vec<CidLink>, DbError>; 419 420 async fn set_record_takedown( 421 &self, 422 cid: &CidLink, 423 takedown_ref: Option<&str>, 424 ) -> Result<(), DbError>; 425 426 async fn insert_user_blocks( 427 &self, 428 user_id: Uuid, 429 block_cids: &[Vec<u8>], 430 repo_rev: &Tid, 431 ) -> Result<(), DbError>; 432 433 async fn delete_user_blocks( 434 &self, 435 user_id: Uuid, 436 block_cids: &[Vec<u8>], 437 ) -> Result<(), DbError>; 438 439 async fn get_user_block_cids_since_rev( 440 &self, 441 user_id: Uuid, 442 since_rev: Option<&Tid>, 443 ) -> Result<Vec<Vec<u8>>, DbError>; 444 445 async fn count_user_blocks(&self, user_id: Uuid) -> Result<i64, DbError>; 446 447 async fn insert_commit_event(&self, data: &CommitEventData) -> Result<(), DbError>; 448 449 async fn insert_identity_event( 450 &self, 451 did: &Did, 452 handle: Option<&Handle>, 453 ) -> Result<(), DbError>; 454 455 async fn insert_account_event(&self, did: &Did, status: AccountStatus) -> Result<(), DbError>; 456 457 async fn insert_sync_event( 458 &self, 459 did: &Did, 460 commit_cid: &CidLink, 461 rev: Option<&Tid>, 462 commit_bytes: &[u8], 463 ) -> Result<(), DbError>; 464 465 async fn insert_genesis_commit_event( 466 &self, 467 did: &Did, 468 commit_cid: &CidLink, 469 mst_root_cid: &CidLink, 470 rev: &Tid, 471 commit_bytes: &[u8], 472 mst_root_bytes: &[u8], 473 ) -> Result<(), DbError>; 474 475 async fn purge_did_events_keeping_latest(&self, did: &Did) -> Result<(), DbError>; 476 477 async fn assign_pending_sequences(&self) -> Result<u64, DbError> { 478 Ok(0) 479 } 480 481 async fn flush_pending_sequences(&self) -> Result<(), DbError> { 482 Ok(()) 483 } 484 485 async fn prune_events_older_than(&self, cutoff: DateTime<Utc>) -> Result<PruneCount, DbError>; 486 487 async fn get_max_seq(&self) -> Result<SequenceNumber, DbError>; 488 489 async fn get_min_seq_since( 490 &self, 491 since: DateTime<Utc>, 492 ) -> Result<Option<SequenceNumber>, DbError>; 493 494 async fn get_account_with_repo(&self, did: &Did) -> Result<Option<RepoAccountInfo>, DbError>; 495 496 async fn get_events_since_seq( 497 &self, 498 since_seq: SequenceNumber, 499 limit: Option<i64>, 500 ) -> Result<Vec<SequencedEvent>, DbError>; 501 502 async fn get_events_in_seq_range( 503 &self, 504 start_seq: SequenceNumber, 505 end_seq: SequenceNumber, 506 ) -> Result<Vec<SequencedEvent>, DbError>; 507 508 async fn get_event_by_seq( 509 &self, 510 seq: SequenceNumber, 511 ) -> Result<Option<SequencedEvent>, DbError>; 512 513 async fn get_events_since_cursor( 514 &self, 515 cursor: SequenceNumber, 516 limit: i64, 517 ) -> Result<Vec<SequencedEvent>, DbError>; 518 519 async fn list_repos_paginated( 520 &self, 521 cursor_did: Option<&Did>, 522 limit: i64, 523 ) -> Result<Vec<RepoListItem>, DbError>; 524 525 async fn get_repo_root_cid_by_user_id(&self, user_id: Uuid) 526 -> Result<Option<CidLink>, DbError>; 527 528 async fn import_repo_data( 529 &self, 530 user_id: Uuid, 531 blocks: &[ImportBlock], 532 records: &[ImportRecord], 533 expected_root_cid: Option<&CidLink>, 534 ) -> Result<(), ImportRepoError>; 535 536 async fn apply_commit( 537 &self, 538 input: ApplyCommitInput, 539 ) -> Result<ApplyCommitResult, ApplyCommitError>; 540 541 async fn get_users_without_blocks(&self) -> Result<Vec<UserWithoutBlocks>, DbError>; 542 543 async fn get_users_needing_record_blobs_backfill( 544 &self, 545 limit: i64, 546 ) -> Result<Vec<UserNeedingRecordBlobsBackfill>, DbError>; 547 548 async fn insert_record_blobs( 549 &self, 550 repo_id: Uuid, 551 record_uris: &[AtUri], 552 blob_cids: &[CidLink], 553 ) -> Result<(), DbError>; 554} 555 556#[async_trait] 557pub trait RepoEventNotifier: Send + Sync { 558 async fn subscribe(&self) -> Result<Box<dyn RepoEventReceiver>, DbError>; 559} 560 561#[async_trait] 562pub trait RepoEventReceiver: Send { 563 async fn recv(&mut self) -> Option<()>; 564}