mirror of
https://github.com/netbirdio/netbird.git
synced 2026-10-27 14:59:09 +01:00
bind saves to loaded profile to prevent cross-profile clobber
This commit is contained in:
@@ -54,28 +54,36 @@ export const useAutostartSetting = () => {
|
|||||||
return ctx;
|
return ctx;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type LoadedConfig = { profileName: string; data: Config };
|
||||||
|
|
||||||
const useSettingsState = () => {
|
const useSettingsState = () => {
|
||||||
const { username, activeProfile, loaded: profileLoaded } = useProfile();
|
const { username, activeProfile, loaded: profileLoaded } = useProfile();
|
||||||
const [config, setConfig] = useState<Config | null>(null);
|
const [loaded, setLoaded] = useState<LoadedConfig | null>(null);
|
||||||
const [guiVersion, setGuiVersion] = useState<string>("—");
|
const [guiVersion, setGuiVersion] = useState<string>("—");
|
||||||
const saveTimer = useRef<ReturnType<typeof setTimeout> | null>(null);
|
const saveTimer = useRef<ReturnType<typeof setTimeout> | null>(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!profileLoaded || !activeProfile) return;
|
if (!profileLoaded || !activeProfile) return;
|
||||||
|
let cancelled = false;
|
||||||
(async () => {
|
(async () => {
|
||||||
try {
|
try {
|
||||||
const c = await SettingsSvc.GetConfig({
|
const data = await SettingsSvc.GetConfig({
|
||||||
profileName: activeProfile,
|
profileName: activeProfile,
|
||||||
username,
|
username,
|
||||||
});
|
});
|
||||||
setConfig(c);
|
if (cancelled) return;
|
||||||
|
setLoaded({ profileName: activeProfile, data });
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
if (cancelled) return;
|
||||||
await errorDialog({
|
await errorDialog({
|
||||||
Title: i18next.t("settings.error.loadTitle"),
|
Title: i18next.t("settings.error.loadTitle"),
|
||||||
Message: errorMessage(e),
|
Message: errorMessage(e),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
|
return () => {
|
||||||
|
cancelled = true;
|
||||||
|
};
|
||||||
}, [profileLoaded, activeProfile, username]);
|
}, [profileLoaded, activeProfile, username]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -96,14 +104,14 @@ const useSettingsState = () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const save = useCallback(
|
const save = useCallback(
|
||||||
async (next: Config) => {
|
async (profileName: string, next: Config) => {
|
||||||
// Sending the "**********" PSK mask back corrupts the stored PSK (wgtypes.ParseKey fails next connect).
|
// Sending the "**********" PSK mask back corrupts the stored PSK (wgtypes.ParseKey fails next connect).
|
||||||
const { preSharedKey, ...rest } = next;
|
const { preSharedKey, ...rest } = next;
|
||||||
try {
|
try {
|
||||||
await SettingsSvc.SetConfig({
|
await SettingsSvc.SetConfig({
|
||||||
...rest,
|
...rest,
|
||||||
...(preSharedKey === "**********" ? {} : { preSharedKey }),
|
...(preSharedKey === "**********" ? {} : { preSharedKey }),
|
||||||
profileName: activeProfile,
|
profileName,
|
||||||
username,
|
username,
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -113,62 +121,62 @@ const useSettingsState = () => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[activeProfile, username],
|
[username],
|
||||||
);
|
);
|
||||||
|
|
||||||
const setField = useCallback(
|
const setField = useCallback(
|
||||||
<K extends keyof Config>(k: K, v: Config[K]) => {
|
<K extends keyof Config>(k: K, v: Config[K]) => {
|
||||||
setConfig((c) => {
|
setLoaded((cur) => {
|
||||||
if (!c) return c;
|
if (!cur) return cur;
|
||||||
const next = { ...c, [k]: v };
|
const next = { ...cur.data, [k]: v };
|
||||||
if (saveTimer.current) clearTimeout(saveTimer.current);
|
if (saveTimer.current) clearTimeout(saveTimer.current);
|
||||||
saveTimer.current = setTimeout(() => {
|
saveTimer.current = setTimeout(() => {
|
||||||
save(next).catch(logSaveError);
|
save(cur.profileName, next).catch(logSaveError);
|
||||||
}, SAVE_DEBOUNCE_MS);
|
}, SAVE_DEBOUNCE_MS);
|
||||||
return next;
|
return { profileName: cur.profileName, data: next };
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
[save],
|
[save],
|
||||||
);
|
);
|
||||||
|
|
||||||
const saveNow = useCallback(async () => {
|
const saveNow = useCallback(async () => {
|
||||||
if (!config) return;
|
if (!loaded) return;
|
||||||
if (saveTimer.current) {
|
if (saveTimer.current) {
|
||||||
clearTimeout(saveTimer.current);
|
clearTimeout(saveTimer.current);
|
||||||
saveTimer.current = null;
|
saveTimer.current = null;
|
||||||
}
|
}
|
||||||
await save(config);
|
await save(loaded.profileName, loaded.data);
|
||||||
}, [config, save]);
|
}, [loaded, save]);
|
||||||
|
|
||||||
const saveField = useCallback(
|
const saveField = useCallback(
|
||||||
async <K extends keyof Config>(k: K, v: Config[K]) => {
|
async <K extends keyof Config>(k: K, v: Config[K]) => {
|
||||||
if (!config) return;
|
if (!loaded) return;
|
||||||
if (saveTimer.current) {
|
if (saveTimer.current) {
|
||||||
clearTimeout(saveTimer.current);
|
clearTimeout(saveTimer.current);
|
||||||
saveTimer.current = null;
|
saveTimer.current = null;
|
||||||
}
|
}
|
||||||
const next = { ...config, [k]: v };
|
const next = { ...loaded.data, [k]: v };
|
||||||
setConfig(next);
|
setLoaded({ profileName: loaded.profileName, data: next });
|
||||||
await save(next);
|
await save(loaded.profileName, next);
|
||||||
},
|
},
|
||||||
[config, save],
|
[loaded, save],
|
||||||
);
|
);
|
||||||
|
|
||||||
const saveFields = useCallback(
|
const saveFields = useCallback(
|
||||||
async (partial: Partial<Config>) => {
|
async (partial: Partial<Config>) => {
|
||||||
if (!config) return;
|
if (!loaded) return;
|
||||||
if (saveTimer.current) {
|
if (saveTimer.current) {
|
||||||
clearTimeout(saveTimer.current);
|
clearTimeout(saveTimer.current);
|
||||||
saveTimer.current = null;
|
saveTimer.current = null;
|
||||||
}
|
}
|
||||||
const next = { ...config, ...partial };
|
const next = { ...loaded.data, ...partial };
|
||||||
setConfig(next);
|
setLoaded({ profileName: loaded.profileName, data: next });
|
||||||
await save(next);
|
await save(loaded.profileName, next);
|
||||||
},
|
},
|
||||||
[config, save],
|
[loaded, save],
|
||||||
);
|
);
|
||||||
|
|
||||||
return { config, guiVersion, setField, saveField, saveFields, saveNow };
|
return { config: loaded?.data ?? null, guiVersion, setField, saveField, saveFields, saveNow };
|
||||||
};
|
};
|
||||||
|
|
||||||
export const SettingsProvider = ({ children }: { children: ReactNode }) => {
|
export const SettingsProvider = ({ children }: { children: ReactNode }) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user