mirror of
https://github.com/zhom/donutbrowser.git
synced 2026-04-30 23:58:11 +02:00
fix: profile sync metadata merge and delta subscription matching
This commit is contained in:
@@ -597,15 +597,35 @@ impl SyncEngine {
|
||||
let _ = self.sync_vpn(vpn_id, Some(app_handle)).await;
|
||||
}
|
||||
|
||||
// Update profile last_sync
|
||||
let mut updated_profile = profile.clone();
|
||||
updated_profile.last_sync = Some(
|
||||
std::time::SystemTime::now()
|
||||
.duration_since(std::time::UNIX_EPOCH)
|
||||
.unwrap()
|
||||
.as_secs(),
|
||||
);
|
||||
let _ = profile_manager.save_profile(&updated_profile);
|
||||
// Download remote metadata and merge changes (name, tags, notes, etc.)
|
||||
let remote_metadata_key = format!("{}profiles/{}/metadata.json", key_prefix, profile_id);
|
||||
if let Ok(remote_meta) = self.download_profile_metadata(&remote_metadata_key).await {
|
||||
let mut updated_profile = profile.clone();
|
||||
// Merge fields that can be changed on other devices
|
||||
updated_profile.name = remote_meta.name;
|
||||
updated_profile.tags = remote_meta.tags;
|
||||
updated_profile.note = remote_meta.note;
|
||||
updated_profile.proxy_id = remote_meta.proxy_id;
|
||||
updated_profile.vpn_id = remote_meta.vpn_id;
|
||||
updated_profile.group_id = remote_meta.group_id;
|
||||
updated_profile.last_sync = Some(
|
||||
std::time::SystemTime::now()
|
||||
.duration_since(std::time::UNIX_EPOCH)
|
||||
.unwrap()
|
||||
.as_secs(),
|
||||
);
|
||||
let _ = profile_manager.save_profile(&updated_profile);
|
||||
} else {
|
||||
// Fallback: just update last_sync
|
||||
let mut updated_profile = profile.clone();
|
||||
updated_profile.last_sync = Some(
|
||||
std::time::SystemTime::now()
|
||||
.duration_since(std::time::UNIX_EPOCH)
|
||||
.unwrap()
|
||||
.as_secs(),
|
||||
);
|
||||
let _ = profile_manager.save_profile(&updated_profile);
|
||||
}
|
||||
let _ = events::emit("profiles-changed", ());
|
||||
|
||||
let _ = events::emit(
|
||||
@@ -691,6 +711,22 @@ impl SyncEngine {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn download_profile_metadata(&self, key: &str) -> SyncResult<BrowserProfile> {
|
||||
let stat = self.client.stat(key).await?;
|
||||
if !stat.exists {
|
||||
return Err(SyncError::InvalidData(
|
||||
"Remote metadata not found".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
let presign = self.client.presign_download(key).await?;
|
||||
let data = self.client.download_bytes(&presign.url).await?;
|
||||
let profile: BrowserProfile = serde_json::from_slice(&data)
|
||||
.map_err(|e| SyncError::SerializationError(format!("Failed to parse metadata: {e}")))?;
|
||||
|
||||
Ok(profile)
|
||||
}
|
||||
|
||||
async fn upload_profile_metadata(
|
||||
&self,
|
||||
profile_id: &str,
|
||||
|
||||
@@ -153,30 +153,20 @@ impl SyncScheduler {
|
||||
}
|
||||
|
||||
pub async fn is_profile_running(&self, profile_id: &str) -> bool {
|
||||
// First check our internal tracking
|
||||
// Check our internal tracking (authoritative — immediately updated by mark_profile_stopped)
|
||||
let running = self.running_profiles.lock().await;
|
||||
if running.contains(profile_id) {
|
||||
return true;
|
||||
}
|
||||
drop(running);
|
||||
|
||||
// Also check the actual profile state from ProfileManager
|
||||
let profile_manager = ProfileManager::instance();
|
||||
if let Ok(profiles) = profile_manager.list_profiles() {
|
||||
if let Some(profile) = profiles.iter().find(|p| p.id.to_string() == profile_id) {
|
||||
if profile.process_id.is_some() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check if locked by another team member (profile in use remotely)
|
||||
if crate::team_lock::TEAM_LOCK
|
||||
// Check if locked by another device (profile in use remotely)
|
||||
if crate::team_lock::PROFILE_LOCK
|
||||
.is_locked_by_another(profile_id)
|
||||
.await
|
||||
{
|
||||
log::debug!(
|
||||
"Profile {} is locked by another team member, treating as running",
|
||||
"Profile {} is locked on another device, treating as running",
|
||||
profile_id
|
||||
);
|
||||
return true;
|
||||
|
||||
@@ -233,10 +233,16 @@ impl SyncSubscription {
|
||||
let key = Self::strip_team_prefix(raw_key);
|
||||
|
||||
let work_item = if key.starts_with("profiles/") {
|
||||
key
|
||||
.strip_prefix("profiles/")
|
||||
.and_then(|s| s.strip_suffix(".tar.gz"))
|
||||
.map(|s| SyncWorkItem::Profile(s.to_string()))
|
||||
// Match both bundle uploads (profiles/{id}.tar.gz) and delta sync updates
|
||||
// (profiles/{id}/manifest.json, profiles/{id}/files/*, profiles/{id}/metadata.json)
|
||||
let profile_id = key.strip_prefix("profiles/").and_then(|rest| {
|
||||
// profiles/{id}.tar.gz → id
|
||||
rest
|
||||
.strip_suffix(".tar.gz")
|
||||
// profiles/{id}/manifest.json → id
|
||||
.or_else(|| rest.split('/').next().filter(|s| !s.is_empty()))
|
||||
});
|
||||
profile_id.map(|s| SyncWorkItem::Profile(s.to_string()))
|
||||
} else if key.starts_with("proxies/") {
|
||||
key
|
||||
.strip_prefix("proxies/")
|
||||
|
||||
Reference in New Issue
Block a user