Skip to content

Commit 26fc469

Browse files
committed
implemented tree spliting to ensure ipfs uploads stay below 1MB limit
1 parent 743dbad commit 26fc469

4 files changed

Lines changed: 626 additions & 57 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
/target
22
.env
3+
/target/release

crates/fula-client/src/encryption.rs

Lines changed: 76 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -795,18 +795,45 @@ impl EncryptedClient {
795795

796796
/// Put an encrypted object using FlatNamespace mode
797797
///
798-
/// This automatically updates the forest index.
798+
/// This automatically updates AND SAVES the forest index after each file.
799+
/// For bulk uploads, use `put_object_flat_deferred` + `flush_forest` instead.
799800
pub async fn put_object_flat(
800801
&self,
801802
bucket: &str,
802803
key: &str,
803804
data: impl Into<Bytes>,
804805
content_type: Option<&str>,
806+
) -> Result<PutObjectResult> {
807+
let result = self.put_object_flat_deferred(bucket, key, data, content_type).await?;
808+
self.flush_forest(bucket).await?;
809+
Ok(result)
810+
}
811+
812+
/// Put an encrypted object using FlatNamespace mode WITHOUT saving forest
813+
///
814+
/// Use this for bulk uploads. Call `flush_forest` after uploading all files
815+
/// to persist the forest index. This is much more efficient for many files.
816+
///
817+
/// # Example
818+
/// ```ignore
819+
/// // Upload 100 files efficiently
820+
/// for file in files {
821+
/// client.put_object_flat_deferred(bucket, &file.path, file.data, None).await?;
822+
/// }
823+
/// // Save forest once at the end
824+
/// client.flush_forest(bucket).await?;
825+
/// ```
826+
pub async fn put_object_flat_deferred(
827+
&self,
828+
bucket: &str,
829+
key: &str,
830+
data: impl Into<Bytes>,
831+
content_type: Option<&str>,
805832
) -> Result<PutObjectResult> {
806833
let data = data.into();
807834
let original_size = data.len() as u64;
808835

809-
// Load or create forest
836+
// Load or create forest (from cache if available)
810837
let mut forest = self.load_forest(bucket).await?;
811838

812839
// Generate a DEK for this object
@@ -833,7 +860,7 @@ impl EncryptedClient {
833860
let encrypted_meta = EncryptedPrivateMetadata::encrypt(&private_meta, &dek)
834861
.map_err(ClientError::Encryption)?;
835862

836-
// Add to forest index
863+
// Add to forest index (in memory only)
837864
let entry = ForestFileEntry::from_metadata(&private_meta, storage_key.clone());
838865
forest.upsert_file(entry);
839866

@@ -856,19 +883,57 @@ impl EncryptedClient {
856883
.with_metadata("x-fula-encrypted", "true")
857884
.with_metadata("x-fula-encryption", &enc_metadata.to_string());
858885

859-
let result = self.inner.put_object_with_metadata(
860-
bucket,
861-
&storage_key,
862-
Bytes::from(ciphertext),
863-
Some(metadata),
864-
).await?;
886+
// Upload with optional pinning
887+
let result = if let Some(ref pinning) = self.pinning {
888+
self.inner.put_object_with_metadata_and_pinning(
889+
bucket,
890+
&storage_key,
891+
Bytes::from(ciphertext),
892+
Some(metadata),
893+
&pinning.endpoint,
894+
&pinning.token,
895+
).await?
896+
} else {
897+
self.inner.put_object_with_metadata(
898+
bucket,
899+
&storage_key,
900+
Bytes::from(ciphertext),
901+
Some(metadata),
902+
).await?
903+
};
865904

866-
// Save updated forest
867-
self.save_forest(bucket, &forest).await?;
905+
// Update cache (but don't save to storage yet)
906+
{
907+
let mut cache = self.forest_cache.write().unwrap();
908+
cache.insert(bucket.to_string(), forest);
909+
}
868910

869911
Ok(result)
870912
}
871913

914+
/// Flush the forest index to storage
915+
///
916+
/// Call this after bulk uploads using `put_object_flat_deferred`.
917+
/// This persists the in-memory forest index to encrypted storage.
918+
pub async fn flush_forest(&self, bucket: &str) -> Result<()> {
919+
let forest = {
920+
let cache = self.forest_cache.read().unwrap();
921+
cache.get(bucket).cloned()
922+
};
923+
924+
if let Some(forest) = forest {
925+
self.save_forest(bucket, &forest).await?;
926+
}
927+
928+
Ok(())
929+
}
930+
931+
/// Check if there are unsaved forest changes
932+
pub fn has_pending_forest_changes(&self, bucket: &str) -> bool {
933+
let cache = self.forest_cache.read().unwrap();
934+
cache.contains_key(bucket)
935+
}
936+
872937
/// Get an object using FlatNamespace mode
873938
///
874939
/// Uses the forest index to resolve original path → storage key.

0 commit comments

Comments
 (0)