refactor: cleanup sync

This commit is contained in:
zhom
2026-08-10 15:47:31 +04:00
parent 929f5a0ead
commit 325d8fae31
16 changed files with 438 additions and 34 deletions
+55 -15
View File
@@ -80,19 +80,49 @@ export function SyncConfigDialog({
const [connectionStatus, setConnectionStatus] = useState<
"unknown" | "testing" | "connected" | "error"
>("unknown");
const [storageEndpoint, setStorageEndpoint] = useState<string | null>(null);
const hasConfig = Boolean(serverUrl && token);
const testConnection = useCallback(async (url: string) => {
setConnectionStatus("testing");
try {
const healthUrl = `${url.replace(/\/$/, "")}/health`;
const response = await fetch(healthUrl);
setConnectionStatus(response.ok ? "connected" : "error");
} catch {
setConnectionStatus("error");
// `/health` is a bare liveness probe: it answers ok on a server whose storage
// is unreachable or misconfigured, which is how a green "connected" could sit
// next to a sync where every single file failed. `/readyz` checks storage and
// reports the endpoint clients are handed in presigned URLs, so surface that
// too — when transfers fail, it is the value worth checking first.
const probeServer = useCallback(async (url: string) => {
const base = url.replace(/\/$/, "");
const response = await fetch(`${base}/readyz`);
// A server old enough to predate /readyz is still a working server, so
// fall back rather than reporting a healthy setup as broken.
if (response.status === 404) {
const health = await fetch(`${base}/health`);
return { ok: health.ok, storageEndpoint: undefined };
}
if (!response.ok) {
return { ok: false as const, storageEndpoint: undefined };
}
const body = (await response.json()) as {
storageEndpoint?: string;
} | null;
return { ok: true as const, storageEndpoint: body?.storageEndpoint };
}, []);
const testConnection = useCallback(
async (url: string) => {
setConnectionStatus("testing");
try {
const result = await probeServer(url);
setStorageEndpoint(result.storageEndpoint ?? null);
setConnectionStatus(result.ok ? "connected" : "error");
} catch {
setStorageEndpoint(null);
setConnectionStatus("error");
}
},
[probeServer],
);
const loadSettings = useCallback(async () => {
setIsLoading(true);
try {
@@ -142,9 +172,9 @@ export function SyncConfigDialog({
setIsTesting(true);
setConnectionStatus("testing");
try {
const healthUrl = `${serverUrl.replace(/\/$/, "")}/health`;
const response = await fetch(healthUrl);
if (response.ok) {
const result = await probeServer(serverUrl);
setStorageEndpoint(result.storageEndpoint ?? null);
if (result.ok) {
setConnectionStatus("connected");
showSuccessToast(t("sync.config.connectionSuccess"));
} else {
@@ -152,12 +182,13 @@ export function SyncConfigDialog({
showErrorToast(t("sync.config.serverError"));
}
} catch {
setStorageEndpoint(null);
setConnectionStatus("error");
showErrorToast(t("sync.config.connectFailed"));
} finally {
setIsTesting(false);
}
}, [serverUrl, t]);
}, [serverUrl, t, probeServer]);
const handleSave = useCallback(async () => {
setIsSaving(true);
@@ -440,9 +471,18 @@ export function SyncConfigDialog({
</div>
)}
{connectionStatus === "connected" && (
<div className="flex items-center gap-2 text-sm text-muted-foreground">
<div className="size-2 rounded-full bg-success" />
{t("sync.status.connected")}
<div className="flex flex-col gap-1">
<div className="flex items-center gap-2 text-sm text-muted-foreground">
<div className="size-2 rounded-full bg-success" />
{t("sync.status.connected")}
</div>
{storageEndpoint && (
<span className="text-xs text-muted-foreground break-all">
{t("sync.config.storageEndpoint", {
endpoint: storageEndpoint,
})}
</span>
)}
</div>
)}
{connectionStatus === "error" && (
+1
View File
@@ -635,6 +635,7 @@
"connectionSuccess": "Connection successful!",
"serverError": "Server responded with an error",
"connectFailed": "Failed to connect to server",
"storageEndpoint": "Storage: {{endpoint}}",
"settingsSaved": "Sync settings saved",
"saveFailed": "Failed to save settings",
"disconnected": "Sync disconnected",
+1
View File
@@ -636,6 +636,7 @@
"connectionSuccess": "¡Conexión exitosa!",
"serverError": "El servidor respondió con un error",
"connectFailed": "Error al conectar con el servidor",
"storageEndpoint": "Almacenamiento: {{endpoint}}",
"settingsSaved": "Ajustes de sincronización guardados",
"saveFailed": "Error al guardar los ajustes",
"disconnected": "Sincronización desconectada",
+1
View File
@@ -636,6 +636,7 @@
"connectionSuccess": "Connexion réussie !",
"serverError": "Le serveur a répondu avec une erreur",
"connectFailed": "Échec de la connexion au serveur",
"storageEndpoint": "Stockage : {{endpoint}}",
"settingsSaved": "Paramètres de synchronisation enregistrés",
"saveFailed": "Échec de lenregistrement des paramètres",
"disconnected": "Synchronisation déconnectée",
+1
View File
@@ -635,6 +635,7 @@
"connectionSuccess": "接続に成功しました!",
"serverError": "サーバーがエラーで応答しました",
"connectFailed": "サーバーへの接続に失敗しました",
"storageEndpoint": "ストレージ: {{endpoint}}",
"settingsSaved": "同期設定を保存しました",
"saveFailed": "設定の保存に失敗しました",
"disconnected": "同期を切断しました",
+1
View File
@@ -635,6 +635,7 @@
"connectionSuccess": "연결 성공!",
"serverError": "서버가 오류로 응답했습니다",
"connectFailed": "서버에 연결하지 못했습니다",
"storageEndpoint": "스토리지: {{endpoint}}",
"settingsSaved": "동기화 설정이 저장되었습니다",
"saveFailed": "설정 저장 실패",
"disconnected": "동기화 연결 끊김",
+1
View File
@@ -636,6 +636,7 @@
"connectionSuccess": "Conexão bem-sucedida!",
"serverError": "O servidor respondeu com um erro",
"connectFailed": "Falha ao conectar ao servidor",
"storageEndpoint": "Armazenamento: {{endpoint}}",
"settingsSaved": "Configurações de sincronização salvas",
"saveFailed": "Falha ao salvar as configurações",
"disconnected": "Sincronização desconectada",
+1
View File
@@ -637,6 +637,7 @@
"connectionSuccess": "Подключение успешно!",
"serverError": "Сервер вернул ошибку",
"connectFailed": "Не удалось подключиться к серверу",
"storageEndpoint": "Хранилище: {{endpoint}}",
"settingsSaved": "Настройки синхронизации сохранены",
"saveFailed": "Не удалось сохранить настройки",
"disconnected": "Синхронизация отключена",
+1
View File
@@ -635,6 +635,7 @@
"connectionSuccess": "Bağlantı başarılı!",
"serverError": "Sunucu bir hatayla yanıt verdi",
"connectFailed": "Sunucuya bağlanılamadı",
"storageEndpoint": "Depolama: {{endpoint}}",
"settingsSaved": "Eşitleme ayarları kaydedildi",
"saveFailed": "Ayarlar kaydedilemedi",
"disconnected": "Eşitleme bağlantısı kesildi",
+1
View File
@@ -635,6 +635,7 @@
"connectionSuccess": "Kết nối thành công!",
"serverError": "Máy chủ trả về lỗi",
"connectFailed": "Kết nối máy chủ thất bại",
"storageEndpoint": "Bộ nhớ: {{endpoint}}",
"settingsSaved": "Đã lưu cài đặt đồng bộ",
"saveFailed": "Lưu cài đặt thất bại",
"disconnected": "Đã ngắt kết nối đồng bộ",
+1
View File
@@ -635,6 +635,7 @@
"connectionSuccess": "连接成功!",
"serverError": "服务器返回了错误",
"connectFailed": "连接服务器失败",
"storageEndpoint": "存储: {{endpoint}}",
"settingsSaved": "同步设置已保存",
"saveFailed": "保存设置失败",
"disconnected": "已断开同步",