fix(config-backup): a restore after a reset keeps the retention, the mode probe cannot write through a planted symlink, the form shows the retention in use

This commit is contained in:
Devvyn committed 2026-10-20 19:31:55 +08:00
1 parent c982fa1ce5
commit 241886d44d
7 files changed
+176 -22

No files matched your search

+7
View File
@@ -1388,6 +1388,13 @@ Object.assign(CodemanApp.prototype, {
const data = body.data;
const dirInput = this.$('appSettingsConfigBackupDir') || document.getElementById('appSettingsConfigBackupDir');
if (dirInput && data.defaultDir) dirInput.placeholder = `default: ${data.defaultDir}`;
// A reset settings.json has none of the four keys, but the passes keep the retention the newest backup
// recorded: show that, or a Save would write 20 / 30 over it.
const saved = this.loadAppSettingsFromStorage();
const countInput = document.getElementById('appSettingsConfigBackupKeepCount');
const daysInput = document.getElementById('appSettingsConfigBackupKeepDays');
if (countInput && saved.configBackupKeepCount === undefined && data.settings) countInput.value = data.settings.keepCount;
if (daysInput && saved.configBackupKeepDays === undefined && data.settings) daysInput.value = data.settings.keepDays;
if (!data.backups.length) {
out.innerHTML = `No backups yet in <code>${escapeHtml(data.dir)}</code>.`;
return;
+6 -8
View File
@@ -40,8 +40,7 @@ import {
resolveConfigBackupDir,
restoreConfigBackup,
configBackupOrigin,
hasConfigBackupSettings,
latestBackupRetention,
withRecordedRetention,
readConfigBackupOrigin,
sweepConfigBackupTemps,
type ConfigBackupResult,
@@ -73,8 +72,10 @@ export interface ConfigBackupStatus {
export async function readEffectiveBackupSettings(): Promise<{ settings: ConfigBackupSettings; dir: string }> {
const raw = await readJsonConfig<Record<string, unknown>>(SETTINGS_PATH, 'settings.json', {});
const settings = readConfigBackupSettings(raw);
return { settings, dir: resolveConfigBackupDir(settings, getDataDir()) };
const base = readConfigBackupSettings(raw);
const dir = resolveConfigBackupDir(base, getDataDir());
// The same fallback the timer pass uses, so the form never shows 20 / 30 while a pass keeps 100 / 0.
return { settings: withRecordedRetention(base, raw, dir, readConfigBackupOrigin(getDataDir())), dir };
}
/** A user-chosen backup folder that is not safe to touch (bounded probe, as for case paths, #516). */
@@ -128,10 +129,7 @@ export async function runConfigBackup(
sweepConfigBackupTemps(dir);
const origin = configBackupOrigin(getDataDir());
// No backup settings in the file (reset, or unreadable): keep the retention the newest snapshot recorded.
const settings =
unreadable || !hasConfigBackupSettings(raw)
? { ...fromFile, ...(latestBackupRetention(dir, origin) ?? {}) }
: fromFile;
const settings = withRecordedRetention(fromFile, unreadable ? {} : raw, dir, origin);
const result = createConfigBackup({
dataDir: getDataDir(),
backupDir: dir,