Client side atproto account migrator in your web browser, along with services for backups and adversarial migrations.
2.6 kB
76 lines
1use crate::db::models::{BlobModel, BlobType};
2use crate::storage::{blob_backup_path, repo_backup_path};
3use anyhow::Result;
4use s3::Bucket;
5use sqlx::{Pool, Postgres};
6
7/// Verifies that all blobs in the database exist in S3.
8/// Returns a Vec of missing blob information (did, cid_or_rev, blob_type).
9pub async fn verify_backups(pool: &Pool<Postgres>, s3_bucket: &Bucket) -> Result<Vec<MissingBlob>> {
10 // Get all blobs from the database
11 let blobs = sqlx::query_as::<_, BlobModel>("SELECT * FROM blobs ORDER BY created_at")
12 .fetch_all(pool)
13 .await?;
14
15 let total_blobs = blobs.len();
16 log::info!("Checking {} blobs in S3...", total_blobs);
17
18 let mut missing_blobs = Vec::new();
19 let mut checked = 0;
20
21 for blob in blobs {
22 checked += 1;
23 if checked % 100 == 0 {
24 log::info!("Checked {}/{} blobs...", checked, total_blobs);
25 }
26
27 let s3_path = match blob.r#type {
28 BlobType::Repo => repo_backup_path(blob.account_did.clone()),
29 BlobType::Blob => blob_backup_path(blob.account_did.clone(), blob.cid_or_rev.clone()),
30 BlobType::Prefs => {
31 // Handle prefs if needed - for now skip
32 log::debug!("Skipping prefs blob: {:?}", blob);
33 continue;
34 }
35 };
36
37 // Check if the object exists in S3
38 match s3_bucket.head_object(&s3_path).await {
39 Ok(_) => {
40 // Object exists, all good
41 log::debug!("✓ Found: {}", s3_path);
42 }
43 Err(e) => {
44 // Check if it's a 404 error (not found)
45 if e.to_string().contains("404") {
46 log::warn!("✗ Missing: {}", s3_path);
47 missing_blobs.push(MissingBlob {
48 did: blob.account_did.clone(),
49 cid_or_rev: blob.cid_or_rev.clone(),
50 blob_type: blob.r#type.clone(),
51 s3_path,
52 });
53 } else {
54 // Some other error - log it but don't count as missing
55 log::error!("Error checking {}: {}", s3_path, e);
56 }
57 }
58 }
59 }
60
61 log::info!(
62 "Verification complete. Checked {} blobs, found {} missing.",
63 checked,
64 missing_blobs.len()
65 );
66
67 Ok(missing_blobs)
68}
69
70#[derive(Debug, Clone)]
71pub struct MissingBlob {
72 pub did: String,
73 pub cid_or_rev: String,
74 pub blob_type: BlobType,
75 pub s3_path: String,
76}