mirror of
https://github.com/zhom/donutbrowser.git
synced 2026-11-14 16:17:18 +01:00
fix: no launch while pulling
This commit is contained in:
1 parent
a36b447360
commit
8ca5a417ac
15 files changed
+66
-17
No files matched your search
@@ -1532,9 +1532,10 @@ pub async fn get_api_server_status() -> Result<Option<u16>, String> {
|
||||
/// apart from a validation failure, cannot drift from the guards that produce
|
||||
/// them. `PROFILE_REMOTE_SYNC_PENDING` in particular is temporary by nature: the
|
||||
/// pull that clears it is already running.
|
||||
const LAUNCH_CONFLICT_CODES: [&str; 6] = [
|
||||
const LAUNCH_CONFLICT_CODES: [&str; 7] = [
|
||||
"PROFILE_RUNNING",
|
||||
"PROFILE_LEASE_HELD",
|
||||
"PROFILE_STILL_SYNCING",
|
||||
"PROFILE_RUNNING_REMOTELY",
|
||||
"PROFILE_REMOTE_SYNC_PENDING",
|
||||
"PROFILE_LOCKED_BY_MEMBER",
|
||||
@@ -7123,6 +7124,7 @@ mod tests {
|
||||
"PROFILE_LOCKED_BY_MEMBER",
|
||||
"PROFILE_LOCKED_ELSEWHERE",
|
||||
"PROFILE_LEASE_HELD",
|
||||
"PROFILE_STILL_SYNCING",
|
||||
] {
|
||||
let (status, body) = manager_error_response(serde_json::json!({ "code": code }).to_string());
|
||||
assert_eq!(status, StatusCode::CONFLICT, "{code} must be a 409");
|
||||
|
||||
@@ -558,15 +558,20 @@ const LAUNCH_PULL_STOP: std::time::Duration = std::time::Duration::from_secs(30)
|
||||
/// Brings the profile up to date before its browser starts, so a device that
|
||||
/// starts after another one never opens an older cookie jar: the site would
|
||||
/// revoke the session on both. A normal sync with the download guard. A
|
||||
/// failure or a timeout launches with what this device has.
|
||||
pub(super) async fn pull_before_launch(app_handle: &tauri::AppHandle, profile: &BrowserProfile) {
|
||||
/// failure or a timeout launches with what this device has, unless the
|
||||
/// timed-out download is still writing into the profile: then the launch is
|
||||
/// refused (`launch_after_pull`).
|
||||
pub(super) async fn pull_before_launch(
|
||||
app_handle: &tauri::AppHandle,
|
||||
profile: &BrowserProfile,
|
||||
) -> Result<(), String> {
|
||||
let started = Instant::now();
|
||||
let profile_id = profile.id.to_string();
|
||||
let engine = match SyncEngine::create_from_settings(app_handle).await {
|
||||
Ok(engine) => engine,
|
||||
Err(e) => {
|
||||
log::warn!("Profile not pulled before launch profile={profile_id} err=\"{e}\"");
|
||||
return;
|
||||
return Ok(());
|
||||
}
|
||||
};
|
||||
let app = app_handle.clone();
|
||||
@@ -590,12 +595,26 @@ pub(super) async fn pull_before_launch(app_handle: &tauri::AppHandle, profile: &
|
||||
Err(_) => {
|
||||
request_sync_cancel(&profile_id);
|
||||
let stopped = tokio::time::timeout(LAUNCH_PULL_STOP, task).await.is_ok();
|
||||
let decision = launch_after_pull(stopped);
|
||||
log::warn!(
|
||||
"Profile pull before launch timed out, launching profile={profile_id} stopped={stopped} elapsed_ms={}",
|
||||
"Profile pull before launch timed out profile={profile_id} stopped={stopped} launch={} elapsed_ms={}",
|
||||
decision.is_ok(),
|
||||
started.elapsed().as_millis()
|
||||
);
|
||||
return decision;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Whether a launch may go on after its pull timed out: never while the
|
||||
/// download can still write into the profile under the browser.
|
||||
fn launch_after_pull(stopped: bool) -> Result<(), String> {
|
||||
if stopped {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(crate::backend_error("PROFILE_STILL_SYNCING"))
|
||||
}
|
||||
}
|
||||
|
||||
/// Notes the copy both sides hold now, and ends a taken-over session's hold
|
||||
@@ -5003,6 +5022,15 @@ pub async fn rollover_encryption_for_all_entities(
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
#[test]
|
||||
fn a_launch_waits_for_a_pull_that_is_still_writing() {
|
||||
assert!(super::launch_after_pull(true).is_ok());
|
||||
assert_eq!(
|
||||
super::launch_after_pull(false).unwrap_err(),
|
||||
r#"{"code":"PROFILE_STILL_SYNCING"}"#
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_resumed_download_skips_a_file_only_at_the_same_content() {
|
||||
let entry = |hash: &str| super::super::manifest::ManifestFileEntry {
|
||||
|
||||
@@ -196,7 +196,11 @@ pub(crate) async fn acquire(profile: &BrowserProfile, force: bool) -> Result<boo
|
||||
// followed by a launch, which pulls then.
|
||||
if taken && !force {
|
||||
if let Some(app) = APP.get() {
|
||||
super::engine::pull_before_launch(app, profile).await;
|
||||
if let Err(refusal) = super::engine::pull_before_launch(app, profile).await {
|
||||
// No browser starts, so the lease is not needed.
|
||||
release(&profile.id.to_string(), "pull_unfinished").await;
|
||||
return Err(refusal);
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(taken)
|
||||
|
||||
@@ -2978,7 +2978,8 @@
|
||||
"siteStorageUnsafePath": "Diese Websitedaten verweisen außerhalb des Profilordners, daher hat Donut sie nicht geändert.",
|
||||
"siteStorageIoFailed": "Donut konnte die Websitedaten nicht ändern: {{detail}}",
|
||||
"agentChatNotFound": "Dieser Chat existiert nicht mehr.",
|
||||
"profileLeaseHeld": "Dieses Profil ist auf {{device}} geöffnet. Es wird um {{time}} frei oder wenn dieses Gerät es schließt."
|
||||
"profileLeaseHeld": "Dieses Profil ist auf {{device}} geöffnet. Es wird um {{time}} frei oder wenn dieses Gerät es schließt.",
|
||||
"profileStillSyncing": "Dieses Profil wird noch synchronisiert. Versuchen Sie es gleich erneut."
|
||||
},
|
||||
"rail": {
|
||||
"profiles": "Profile",
|
||||
|
||||
@@ -2978,7 +2978,8 @@
|
||||
"siteStorageUnsafePath": "This site data links outside the profile folder, so Donut did not change it.",
|
||||
"siteStorageIoFailed": "Donut could not change the site data: {{detail}}",
|
||||
"agentChatNotFound": "This chat no longer exists.",
|
||||
"profileLeaseHeld": "This profile is open on {{device}}. It becomes free at {{time}}, or when that device closes it."
|
||||
"profileLeaseHeld": "This profile is open on {{device}}. It becomes free at {{time}}, or when that device closes it.",
|
||||
"profileStillSyncing": "This profile is still syncing. Try again shortly."
|
||||
},
|
||||
"rail": {
|
||||
"profiles": "Profiles",
|
||||
|
||||
@@ -2998,7 +2998,8 @@
|
||||
"siteStorageUnsafePath": "Estos datos del sitio apuntan fuera de la carpeta del perfil, así que Donut no los cambió.",
|
||||
"siteStorageIoFailed": "Donut no pudo cambiar los datos del sitio: {{detail}}",
|
||||
"agentChatNotFound": "Este chat ya no existe.",
|
||||
"profileLeaseHeld": "Este perfil está abierto en {{device}}. Quedará libre a las {{time}} o cuando ese dispositivo lo cierre."
|
||||
"profileLeaseHeld": "Este perfil está abierto en {{device}}. Quedará libre a las {{time}} o cuando ese dispositivo lo cierre.",
|
||||
"profileStillSyncing": "Este perfil aún se está sincronizando. Inténtalo de nuevo en breve."
|
||||
},
|
||||
"rail": {
|
||||
"profiles": "Perfiles",
|
||||
|
||||
@@ -2998,7 +2998,8 @@
|
||||
"siteStorageUnsafePath": "Ces données de site pointent hors du dossier du profil, Donut ne les a donc pas modifiées.",
|
||||
"siteStorageIoFailed": "Donut n'a pas pu modifier les données du site : {{detail}}",
|
||||
"agentChatNotFound": "Ce chat n’existe plus.",
|
||||
"profileLeaseHeld": "Ce profil est ouvert sur {{device}}. Il sera libre à {{time}}, ou quand cet appareil le fermera."
|
||||
"profileLeaseHeld": "Ce profil est ouvert sur {{device}}. Il sera libre à {{time}}, ou quand cet appareil le fermera.",
|
||||
"profileStillSyncing": "Ce profil est encore en cours de synchronisation. Réessayez dans un instant."
|
||||
},
|
||||
"rail": {
|
||||
"profiles": "Profils",
|
||||
|
||||
@@ -2978,7 +2978,8 @@
|
||||
"siteStorageUnsafePath": "このサイトデータはプロファイルフォルダーの外を指しているため、Donut は変更しませんでした。",
|
||||
"siteStorageIoFailed": "Donut はサイトデータを変更できませんでした: {{detail}}",
|
||||
"agentChatNotFound": "このチャットはもう存在しません。",
|
||||
"profileLeaseHeld": "このプロファイルは {{device}} で開かれています。{{time}} またはそのデバイスが閉じると使用できます。"
|
||||
"profileLeaseHeld": "このプロファイルは {{device}} で開かれています。{{time}} またはそのデバイスが閉じると使用できます。",
|
||||
"profileStillSyncing": "このプロファイルはまだ同期中です。しばらくしてからもう一度お試しください。"
|
||||
},
|
||||
"rail": {
|
||||
"profiles": "プロファイル",
|
||||
|
||||
@@ -2978,7 +2978,8 @@
|
||||
"siteStorageUnsafePath": "이 사이트 데이터가 프로필 폴더 밖을 가리켜 Donut이 변경하지 않았습니다.",
|
||||
"siteStorageIoFailed": "Donut이 사이트 데이터를 변경하지 못했습니다: {{detail}}",
|
||||
"agentChatNotFound": "이 채팅은 더 이상 없습니다.",
|
||||
"profileLeaseHeld": "이 프로필은 {{device}}에서 열려 있습니다. {{time}}에 또는 해당 기기가 닫으면 사용할 수 있습니다."
|
||||
"profileLeaseHeld": "이 프로필은 {{device}}에서 열려 있습니다. {{time}}에 또는 해당 기기가 닫으면 사용할 수 있습니다.",
|
||||
"profileStillSyncing": "이 프로필은 아직 동기화 중입니다. 잠시 후 다시 시도하세요."
|
||||
},
|
||||
"rail": {
|
||||
"profiles": "프로필",
|
||||
|
||||
@@ -2998,7 +2998,8 @@
|
||||
"siteStorageUnsafePath": "Estes dados do site apontam para fora da pasta do perfil, então o Donut não os alterou.",
|
||||
"siteStorageIoFailed": "O Donut não conseguiu alterar os dados do site: {{detail}}",
|
||||
"agentChatNotFound": "Este chat não existe mais.",
|
||||
"profileLeaseHeld": "Este perfil está aberto em {{device}}. Fica livre às {{time}} ou quando esse dispositivo o fechar."
|
||||
"profileLeaseHeld": "Este perfil está aberto em {{device}}. Fica livre às {{time}} ou quando esse dispositivo o fechar.",
|
||||
"profileStillSyncing": "Este perfil ainda está a sincronizar. Tente novamente daqui a pouco."
|
||||
},
|
||||
"rail": {
|
||||
"profiles": "Perfis",
|
||||
|
||||
@@ -3018,7 +3018,8 @@
|
||||
"siteStorageUnsafePath": "Эти данные сайта указывают за пределы папки профиля, поэтому Donut их не изменил.",
|
||||
"siteStorageIoFailed": "Donut не удалось изменить данные сайта: {{detail}}",
|
||||
"agentChatNotFound": "Этого чата больше нет.",
|
||||
"profileLeaseHeld": "Этот профиль открыт на {{device}}. Он освободится в {{time}} или когда это устройство его закроет."
|
||||
"profileLeaseHeld": "Этот профиль открыт на {{device}}. Он освободится в {{time}} или когда это устройство его закроет.",
|
||||
"profileStillSyncing": "Этот профиль ещё синхронизируется. Повторите попытку чуть позже."
|
||||
},
|
||||
"rail": {
|
||||
"profiles": "Профили",
|
||||
|
||||
@@ -2978,7 +2978,8 @@
|
||||
"siteStorageUnsafePath": "Bu site verisi profil klasörünün dışını gösteriyor, bu yüzden Donut onu değiştirmedi.",
|
||||
"siteStorageIoFailed": "Donut site verisini değiştiremedi: {{detail}}",
|
||||
"agentChatNotFound": "Bu sohbet artık yok.",
|
||||
"profileLeaseHeld": "Bu profil {{device}} üzerinde açık. {{time}} saatinde veya o cihaz kapattığında serbest kalır."
|
||||
"profileLeaseHeld": "Bu profil {{device}} üzerinde açık. {{time}} saatinde veya o cihaz kapattığında serbest kalır.",
|
||||
"profileStillSyncing": "Bu profil hâlâ eşitleniyor. Birazdan yeniden deneyin."
|
||||
},
|
||||
"rail": {
|
||||
"profiles": "Profiller",
|
||||
|
||||
@@ -2978,7 +2978,8 @@
|
||||
"siteStorageUnsafePath": "Dữ liệu trang này trỏ ra ngoài thư mục hồ sơ, nên Donut không thay đổi nó.",
|
||||
"siteStorageIoFailed": "Donut không thể thay đổi dữ liệu trang: {{detail}}",
|
||||
"agentChatNotFound": "Cuộc trò chuyện này không còn nữa.",
|
||||
"profileLeaseHeld": "Hồ sơ này đang mở trên {{device}}. Hồ sơ sẽ trống lúc {{time}} hoặc khi thiết bị đó đóng nó."
|
||||
"profileLeaseHeld": "Hồ sơ này đang mở trên {{device}}. Hồ sơ sẽ trống lúc {{time}} hoặc khi thiết bị đó đóng nó.",
|
||||
"profileStillSyncing": "Hồ sơ này vẫn đang đồng bộ. Hãy thử lại sau ít phút."
|
||||
},
|
||||
"rail": {
|
||||
"profiles": "Profile",
|
||||
|
||||
@@ -2978,7 +2978,8 @@
|
||||
"siteStorageUnsafePath": "此网站数据指向配置文件夹之外,因此 Donut 未作更改。",
|
||||
"siteStorageIoFailed": "Donut 无法更改网站数据:{{detail}}",
|
||||
"agentChatNotFound": "此对话已不存在。",
|
||||
"profileLeaseHeld": "此配置文件已在 {{device}} 上打开。它将在 {{time}} 或该设备关闭它时释放。"
|
||||
"profileLeaseHeld": "此配置文件已在 {{device}} 上打开。它将在 {{time}} 或该设备关闭它时释放。",
|
||||
"profileStillSyncing": "此配置文件仍在同步。请稍后再试。"
|
||||
},
|
||||
"rail": {
|
||||
"profiles": "配置文件",
|
||||
|
||||
@@ -244,6 +244,8 @@ export type BackendErrorCode =
|
||||
// `params.device` names it, `params.expiresAt` is when its lease ends,
|
||||
// `params.canTakeOver` is "true" for this account's own device.
|
||||
| "PROFILE_LEASE_HELD"
|
||||
// The pull before a launch timed out and its download had not stopped.
|
||||
| "PROFILE_STILL_SYNCING"
|
||||
| "NOT_TEAM_MEMBER"
|
||||
| "COOKIE_BOT_NOT_ENTITLED"
|
||||
| "COOKIE_BOT_NOT_ENROLLED"
|
||||
@@ -1030,6 +1032,8 @@ export function translateBackendError(t: TFunction, err: unknown): string {
|
||||
device: parsed.params?.device ?? "",
|
||||
time: leaseExpiryTime(parsed.params?.expiresAt),
|
||||
});
|
||||
case "PROFILE_STILL_SYNCING":
|
||||
return t("backendErrors.profileStillSyncing");
|
||||
case "PROFILE_LOCK_UNAVAILABLE":
|
||||
return t("backendErrors.profileLockUnavailable");
|
||||
case "NOT_TEAM_MEMBER":
|
||||
|
||||
Reference in new issue
Block a user