mirror of
https://github.com/zhom/donutbrowser.git
synced 2026-10-19 22:17:28 +02:00
feat: cross-OS profile sync
This commit is contained in:
1 parent
b7a89ac94e
commit
c359049e09
28 files changed
+532
-122
No files matched your search
@@ -86,7 +86,7 @@ import { LaunchArgumentsEditor } from "@/components/wayfern-config-form";
|
||||
import { WindowDragArea } from "@/components/window-drag-area";
|
||||
import { useInputModality } from "@/hooks/use-input-modality";
|
||||
import { translateBackendError } from "@/lib/backend-errors";
|
||||
import { getProfileIcon } from "@/lib/browser-utils";
|
||||
import { getProfileIcon, getProfileNativeOs } from "@/lib/browser-utils";
|
||||
import { DNS_BLOCKLIST_LEVELS } from "@/lib/dns-blocklist-levels";
|
||||
import { formatRelativeTime } from "@/lib/flag-utils";
|
||||
import { MOTION_EASE_OUT } from "@/lib/motion";
|
||||
@@ -2297,6 +2297,7 @@ function FingerprintSectionInline({
|
||||
profileVersion={profile.version}
|
||||
profileBrowser={profile.browser}
|
||||
profileId={profile.id}
|
||||
nativeOS={getProfileNativeOs(profile)}
|
||||
/>
|
||||
) : (
|
||||
<>
|
||||
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
} from "@/components/ui/dialog";
|
||||
import { ScrollArea } from "@/components/ui/scroll-area";
|
||||
import { translateBackendError } from "@/lib/backend-errors";
|
||||
import { getProfileNativeOs } from "@/lib/browser-utils";
|
||||
import type { BrowserProfile, WayfernConfig, WayfernOS } from "@/types";
|
||||
import { LoadingButton } from "./loading-button";
|
||||
import { RippleButton } from "./ui/ripple";
|
||||
@@ -51,10 +52,8 @@ export function WayfernConfigDialog({
|
||||
useEffect(() => {
|
||||
if (profile?.browser === "wayfern") {
|
||||
setConfig(
|
||||
profile.wayfern_config || {
|
||||
geoip: true,
|
||||
os: getCurrentOS(),
|
||||
},
|
||||
// No `os` means the profile's native OS, which the form shows.
|
||||
profile.wayfern_config || { geoip: true },
|
||||
);
|
||||
}
|
||||
}, [profile]);
|
||||
@@ -96,12 +95,7 @@ export function WayfernConfigDialog({
|
||||
|
||||
const handleClose = () => {
|
||||
if (profile?.browser === "wayfern") {
|
||||
setConfig(
|
||||
profile.wayfern_config || {
|
||||
geoip: true,
|
||||
os: getCurrentOS(),
|
||||
},
|
||||
);
|
||||
setConfig(profile.wayfern_config || { geoip: true });
|
||||
}
|
||||
onClose();
|
||||
};
|
||||
@@ -139,6 +133,7 @@ export function WayfernConfigDialog({
|
||||
profileVersion={profile.version}
|
||||
profileBrowser="wayfern"
|
||||
profileId={profile.id}
|
||||
nativeOS={getProfileNativeOs(profile)}
|
||||
/>
|
||||
</div>
|
||||
</ScrollArea>
|
||||
|
||||
@@ -183,6 +183,10 @@ interface WayfernConfigFormProps {
|
||||
/// The saved profile whose persona is edited here. Absent while a profile is
|
||||
/// being created: there is no seed yet, so there is no person to show.
|
||||
profileId?: string;
|
||||
/// The OS a fingerprint without an explicit `os` describes: the machine that
|
||||
/// created the saved profile. It stays the profile's claim on every
|
||||
/// computer, so it is shown and regenerated instead of this computer's OS.
|
||||
nativeOS?: WayfernOS;
|
||||
}
|
||||
|
||||
export function LaunchArgumentsEditor({
|
||||
@@ -281,6 +285,7 @@ export function WayfernConfigForm({
|
||||
profileVersion,
|
||||
profileBrowser,
|
||||
profileId,
|
||||
nativeOS,
|
||||
}: WayfernConfigFormProps) {
|
||||
const { t } = useTranslation();
|
||||
const [activeTab, setActiveTab] = useState(
|
||||
@@ -296,7 +301,10 @@ export function WayfernConfigForm({
|
||||
if (!profileVersion) return;
|
||||
setIsGeneratingFingerprint(true);
|
||||
try {
|
||||
const configJson = JSON.stringify(config);
|
||||
const configJson = JSON.stringify({
|
||||
...config,
|
||||
os: config.os ?? nativeOS,
|
||||
});
|
||||
const result = await invoke<GeneratedFingerprint>(
|
||||
"generate_sample_fingerprint",
|
||||
{
|
||||
@@ -341,7 +349,7 @@ export function WayfernConfigForm({
|
||||
void handleGenerateFingerprint();
|
||||
};
|
||||
|
||||
const selectedOS = config.os || currentOS;
|
||||
const selectedOS = config.os || nativeOS || currentOS;
|
||||
|
||||
useEffect(() => {
|
||||
if (isCreating && typeof window !== "undefined") {
|
||||
|
||||
@@ -2307,6 +2307,7 @@
|
||||
"cookieBotTouchFingerprintUnsupported": "Dieses Profil gibt sich als Touch-Gerät aus, das der Bot nicht steuern kann. Verwenden Sie einen Desktop-Fingerprint.",
|
||||
"profileRunningRemotely": "Dieses Profil läuft auf einem entfernten Rechner. Stoppen Sie zuerst die Remote-Sitzung.",
|
||||
"profileRemoteSyncPending": "Eine Remote-Sitzung wurde gerade beendet. Ihre Änderungen werden heruntergeladen, bevor dieses Profil hier geöffnet werden kann.",
|
||||
"profileDataNotDownloaded": "Die Dateien dieses Profils wurden noch nicht auf diesen Computer heruntergeladen. Prüfen Sie, ob die Synchronisierung verbunden ist, und versuchen Sie es dann erneut.",
|
||||
"profileLockedByMember": "Dieses Profil wird von {{email}} verwendet.",
|
||||
"profileLockedElsewhere": "Dieses Profil wird auf einem anderen Gerät verwendet.",
|
||||
"profileLockUnavailable": "Es konnte nicht geprüft werden, ob dieses Profil an anderer Stelle verwendet wird. Prüfen Sie Ihre Verbindung und versuchen Sie es erneut.",
|
||||
|
||||
@@ -2307,6 +2307,7 @@
|
||||
"cookieBotTouchFingerprintUnsupported": "This profile claims a touch device, which the bot cannot drive. Use a desktop fingerprint.",
|
||||
"profileRunningRemotely": "This profile is running on a remote machine. Stop the remote session first.",
|
||||
"profileRemoteSyncPending": "A remote session just finished. Waiting for its changes to download before this profile can open here.",
|
||||
"profileDataNotDownloaded": "This profile's files have not downloaded to this computer yet. Check that sync is connected, then try again.",
|
||||
"profileLockedByMember": "This profile is in use by {{email}}.",
|
||||
"profileLockedElsewhere": "This profile is in use on another device.",
|
||||
"profileLockUnavailable": "Could not check whether this profile is in use elsewhere. Check your connection and try again.",
|
||||
|
||||
@@ -2317,6 +2317,7 @@
|
||||
"cookieBotTouchFingerprintUnsupported": "Este perfil declara un dispositivo táctil, que el bot no puede controlar. Usa una huella de escritorio.",
|
||||
"profileRunningRemotely": "Este perfil se está ejecutando en una máquina remota. Detén primero la sesión remota.",
|
||||
"profileRemoteSyncPending": "Una sesión remota acaba de terminar. Esperando a que se descarguen sus cambios antes de abrir este perfil aquí.",
|
||||
"profileDataNotDownloaded": "Los archivos de este perfil aún no se han descargado en este equipo. Comprueba que la sincronización esté conectada e inténtalo de nuevo.",
|
||||
"profileLockedByMember": "Este perfil está siendo usado por {{email}}.",
|
||||
"profileLockedElsewhere": "Este perfil está en uso en otro dispositivo.",
|
||||
"profileLockUnavailable": "No se pudo comprobar si este perfil está en uso en otro lugar. Revisa tu conexión e inténtalo de nuevo.",
|
||||
|
||||
@@ -2317,6 +2317,7 @@
|
||||
"cookieBotTouchFingerprintUnsupported": "Ce profil déclare un appareil tactile, que le bot ne peut pas piloter. Utilisez une empreinte de bureau.",
|
||||
"profileRunningRemotely": "Ce profil s'exécute sur une machine distante. Arrêtez d'abord la session distante.",
|
||||
"profileRemoteSyncPending": "Une session distante vient de se terminer. Ses modifications doivent être téléchargées avant d'ouvrir ce profil ici.",
|
||||
"profileDataNotDownloaded": "Les fichiers de ce profil n'ont pas encore été téléchargés sur cet ordinateur. Vérifiez que la synchronisation est connectée, puis réessayez.",
|
||||
"profileLockedByMember": "Ce profil est utilisé par {{email}}.",
|
||||
"profileLockedElsewhere": "Ce profil est utilisé sur un autre appareil.",
|
||||
"profileLockUnavailable": "Impossible de vérifier si ce profil est utilisé ailleurs. Vérifiez votre connexion et réessayez.",
|
||||
|
||||
@@ -2307,6 +2307,7 @@
|
||||
"cookieBotTouchFingerprintUnsupported": "このプロファイルはタッチ端末を名乗っており、ボットは操作できません。デスクトップのフィンガープリントをお使いください。",
|
||||
"profileRunningRemotely": "このプロファイルはリモートマシンで実行中です。先にリモートセッションを停止してください。",
|
||||
"profileRemoteSyncPending": "リモートセッションが終了しました。この profile をここで開く前に、変更のダウンロードを待っています。",
|
||||
"profileDataNotDownloaded": "このプロファイルのファイルはまだこのコンピューターにダウンロードされていません。同期が接続されていることを確認してから、もう一度お試しください。",
|
||||
"profileLockedByMember": "このプロファイルは {{email}} が使用中です。",
|
||||
"profileLockedElsewhere": "このプロファイルは別のデバイスで使用中です。",
|
||||
"profileLockUnavailable": "このプロファイルが他で使用中か確認できませんでした。接続を確認して再試行してください。",
|
||||
|
||||
@@ -2307,6 +2307,7 @@
|
||||
"cookieBotTouchFingerprintUnsupported": "이 프로필은 터치 기기를 표방하며, 봇이 조작할 수 없습니다. 데스크톱 지문을 사용하세요.",
|
||||
"profileRunningRemotely": "이 프로필은 원격 머신에서 실행 중입니다. 먼저 원격 세션을 중지하세요.",
|
||||
"profileRemoteSyncPending": "원격 세션이 방금 끝났습니다. 이 프로필을 여기서 열기 전에 변경 사항을 내려받는 중입니다.",
|
||||
"profileDataNotDownloaded": "이 프로필의 파일이 아직 이 컴퓨터로 내려받아지지 않았습니다. 동기화가 연결되어 있는지 확인한 후 다시 시도하세요.",
|
||||
"profileLockedByMember": "이 프로필은 {{email}} 님이 사용 중입니다.",
|
||||
"profileLockedElsewhere": "이 프로필은 다른 기기에서 사용 중입니다.",
|
||||
"profileLockUnavailable": "이 프로필이 다른 곳에서 사용 중인지 확인할 수 없습니다. 연결을 확인한 뒤 다시 시도하세요.",
|
||||
|
||||
@@ -2317,6 +2317,7 @@
|
||||
"cookieBotTouchFingerprintUnsupported": "Este perfil declara um dispositivo de toque, que o bot não consegue controlar. Use uma impressão digital de computador.",
|
||||
"profileRunningRemotely": "Este perfil está em execução numa máquina remota. Pare primeiro a sessão remota.",
|
||||
"profileRemoteSyncPending": "Uma sessão remota acabou de terminar. A aguardar a transferência das alterações antes de abrir este perfil aqui.",
|
||||
"profileDataNotDownloaded": "Os ficheiros deste perfil ainda não foram transferidos para este computador. Verifique se a sincronização está ligada e tente novamente.",
|
||||
"profileLockedByMember": "Este perfil está a ser utilizado por {{email}}.",
|
||||
"profileLockedElsewhere": "Este perfil está a ser utilizado noutro dispositivo.",
|
||||
"profileLockUnavailable": "Não foi possível verificar se este perfil está a ser utilizado noutro local. Verifique a ligação e tente novamente.",
|
||||
|
||||
@@ -2327,6 +2327,7 @@
|
||||
"cookieBotTouchFingerprintUnsupported": "Этот профиль выдаёт себя за сенсорное устройство, которым бот управлять не может. Используйте настольный отпечаток.",
|
||||
"profileRunningRemotely": "Этот профиль запущен на удалённой машине. Сначала остановите удалённый сеанс.",
|
||||
"profileRemoteSyncPending": "Удалённый сеанс только что завершился. Дождитесь загрузки его изменений, прежде чем открывать профиль здесь.",
|
||||
"profileDataNotDownloaded": "Файлы этого профиля ещё не загружены на этот компьютер. Проверьте подключение синхронизации и попробуйте снова.",
|
||||
"profileLockedByMember": "Этот профиль используется пользователем {{email}}.",
|
||||
"profileLockedElsewhere": "Этот профиль используется на другом устройстве.",
|
||||
"profileLockUnavailable": "Не удалось проверить, используется ли профиль где-то ещё. Проверьте подключение и попробуйте снова.",
|
||||
|
||||
@@ -2307,6 +2307,7 @@
|
||||
"cookieBotTouchFingerprintUnsupported": "Bu profil dokunmatik bir cihaz olduğunu bildiriyor ve bot bunu süremez. Masaüstü parmak izi kullanın.",
|
||||
"profileRunningRemotely": "Bu profil uzak bir makinede çalışıyor. Önce uzak oturumu durdurun.",
|
||||
"profileRemoteSyncPending": "Uzak oturum az önce bitti. Bu profili burada açmadan önce değişikliklerinin inmesi bekleniyor.",
|
||||
"profileDataNotDownloaded": "Bu profilin dosyaları henüz bu bilgisayara inmedi. Eşitlemenin bağlı olduğunu kontrol edip tekrar deneyin.",
|
||||
"profileLockedByMember": "Bu profil {{email}} tarafından kullanılıyor.",
|
||||
"profileLockedElsewhere": "Bu profil başka bir cihazda kullanılıyor.",
|
||||
"profileLockUnavailable": "Bu profilin başka bir yerde kullanılıp kullanılmadığı denetlenemedi. Bağlantınızı kontrol edip yeniden deneyin.",
|
||||
|
||||
@@ -2307,6 +2307,7 @@
|
||||
"cookieBotTouchFingerprintUnsupported": "Hồ sơ này khai báo là thiết bị cảm ứng, bot không điều khiển được. Hãy dùng vân tay máy tính để bàn.",
|
||||
"profileRunningRemotely": "Hồ sơ này đang chạy trên máy từ xa. Hãy dừng phiên từ xa trước.",
|
||||
"profileRemoteSyncPending": "Một phiên từ xa vừa kết thúc. Đang chờ tải các thay đổi về trước khi mở hồ sơ này tại đây.",
|
||||
"profileDataNotDownloaded": "Các tệp của hồ sơ này chưa được tải về máy tính này. Hãy kiểm tra kết nối đồng bộ rồi thử lại.",
|
||||
"profileLockedByMember": "Hồ sơ này đang được {{email}} sử dụng.",
|
||||
"profileLockedElsewhere": "Hồ sơ này đang được sử dụng trên thiết bị khác.",
|
||||
"profileLockUnavailable": "Không thể kiểm tra hồ sơ này có đang được dùng ở nơi khác hay không. Hãy kiểm tra kết nối và thử lại.",
|
||||
|
||||
@@ -2307,6 +2307,7 @@
|
||||
"cookieBotTouchFingerprintUnsupported": "该配置文件声称是触摸设备,机器人无法操作。请使用桌面端指纹。",
|
||||
"profileRunningRemotely": "该配置文件正在远程计算机上运行。请先停止远程会话。",
|
||||
"profileRemoteSyncPending": "远程会话刚刚结束。正在等待其更改下载完成后才能在此打开该配置文件。",
|
||||
"profileDataNotDownloaded": "此配置文件的文件尚未下载到这台电脑。请确认同步已连接,然后重试。",
|
||||
"profileLockedByMember": "该配置文件正在被 {{email}} 使用。",
|
||||
"profileLockedElsewhere": "该配置文件正在另一台设备上使用。",
|
||||
"profileLockUnavailable": "无法检查该配置文件是否正在别处使用。请检查网络连接后重试。",
|
||||
|
||||
@@ -155,6 +155,7 @@ export type BackendErrorCode =
|
||||
| "REMOTE_HOURS_EXHAUSTED"
|
||||
| "PROFILE_RUNNING_REMOTELY"
|
||||
| "PROFILE_REMOTE_SYNC_PENDING"
|
||||
| "PROFILE_DATA_NOT_DOWNLOADED"
|
||||
| "PROFILE_LOCKED_BY_MEMBER"
|
||||
| "PROFILE_LOCKED_ELSEWHERE"
|
||||
| "PROFILE_LOCK_UNAVAILABLE"
|
||||
@@ -670,6 +671,8 @@ export function translateBackendError(t: TFunction, err: unknown): string {
|
||||
return t("backendErrors.profileRunningRemotely");
|
||||
case "PROFILE_REMOTE_SYNC_PENDING":
|
||||
return t("backendErrors.profileRemoteSyncPending");
|
||||
case "PROFILE_DATA_NOT_DOWNLOADED":
|
||||
return t("backendErrors.profileDataNotDownloaded");
|
||||
case "PROFILE_LOCKED_BY_MEMBER":
|
||||
return t("backendErrors.profileLockedByMember", {
|
||||
email: parsed.params?.email ?? "",
|
||||
|
||||
@@ -90,10 +90,29 @@ export function getProfileIcon(profile: {
|
||||
return getBrowserIcon(profile.browser);
|
||||
}
|
||||
|
||||
/**
|
||||
* The OS a fingerprint without an explicit `os` was generated for: the machine
|
||||
* that created the profile. Mirrors `BrowserProfile::native_os`, without its
|
||||
* fallback to this computer, so callers can tell "unknown" apart.
|
||||
*/
|
||||
export function getProfileNativeOs(profile: {
|
||||
host_os?: string;
|
||||
}): "macos" | "windows" | "linux" | undefined {
|
||||
const os = profile.host_os;
|
||||
return os === "macos" || os === "windows" || os === "linux" ? os : undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether a profile cannot run on this computer because a browser bound to
|
||||
* its OS created it elsewhere. Mirrors `BrowserProfile::is_cross_os`: a
|
||||
* Wayfern profile runs on every OS, so it is never cross-OS.
|
||||
*/
|
||||
export function isCrossOsProfile(profile: {
|
||||
browser: string;
|
||||
host_os?: string;
|
||||
wayfern_config?: { os?: string };
|
||||
}): boolean {
|
||||
if (profile.browser === "wayfern") return false;
|
||||
const profileOs = profile.host_os || profile.wayfern_config?.os;
|
||||
if (!profileOs) return false;
|
||||
return profileOs !== getCurrentOS();
|
||||
|
||||
Reference in new issue
Block a user