Ferndiagnose-Backup: auch Full-Backup-Import schreibt die MCP-Dateien
Advisor-Fund: der Settings-only-Import stellte die Ferndiagnose-Einstellungen wieder her, der Full-Backup-Import (Download-Liste mitgesichert) aber NICHT — er liess die debug_*-Dateien unangetastet, und der nachfolgende Relaunch bootete mit den alten Werten. Damit waere "MCP-Settings im Backup" auf dem Full-Backup-Pfad still gescheitert. Fix: restoreMcpRemoteFromBackup nimmt jetzt restartNow. Settings-only ruft mit true (laufenden Server sofort neu laden), Full-Backup mit false (nur Dateien schreiben — der Relaunch-Boot liest sie via startDebugServer). Beide Pfade tragen die Sektion jetzt. Zusatz-Tests: normalizeSettings bewahrt backupIncludeMcp (der Dreh- und Angelpunkt — ohne das wuerde der Schalter bei jedem Save auf false zuruecksetzen und das Feature tot sein); Full-Backup-Write-Pfad schreibt debug_host/port/allowlist auf Platte ohne Restart (In-Memory bleibt stale → Boot uebernimmt). Suite 919 gruen, tsc=6.
This commit is contained in:
parent
a35ddf68b2
commit
d9657a5459
@ -372,17 +372,20 @@ export class AppController {
|
|||||||
return this.getRemoteDiagnostics();
|
return this.getRemoteDiagnostics();
|
||||||
}
|
}
|
||||||
|
|
||||||
private restoreMcpRemoteFromBackup(section: unknown): void {
|
private restoreMcpRemoteFromBackup(section: unknown, restartNow: boolean): void {
|
||||||
const restore = resolveMcpRemoteRestore(section);
|
const restore = resolveMcpRemoteRestore(section);
|
||||||
if (!restore) {
|
if (!restore) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
writeDebugServerConfig({ host: restore.host, port: restore.port, allowlist: restore.allowlist });
|
writeDebugServerConfig({ host: restore.host, port: restore.port, allowlist: restore.allowlist });
|
||||||
|
if (restartNow) {
|
||||||
void restartDebugServer().catch(() => {});
|
void restartDebugServer().catch(() => {});
|
||||||
|
}
|
||||||
this.audit("INFO", "Ferndiagnose-Einstellungen aus Backup wiederhergestellt", {
|
this.audit("INFO", "Ferndiagnose-Einstellungen aus Backup wiederhergestellt", {
|
||||||
port: restore.port ?? null,
|
port: restore.port ?? null,
|
||||||
allowlistCount: restore.allowlist?.length ?? 0,
|
allowlistCount: restore.allowlist?.length ?? 0,
|
||||||
host: restore.host ?? "unveraendert"
|
host: restore.host ?? "unveraendert",
|
||||||
|
restartNow
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -827,7 +830,7 @@ public async checkDebridAccounts(): Promise<DebridAccountStatus[]> {
|
|||||||
this.settings = restoredSettings;
|
this.settings = restoredSettings;
|
||||||
saveSettings(this.storagePaths, this.settings);
|
saveSettings(this.storagePaths, this.settings);
|
||||||
this.manager.setSettings(this.settings, { suppressRetroactiveCleanup: true });
|
this.manager.setSettings(this.settings, { suppressRetroactiveCleanup: true });
|
||||||
this.restoreMcpRemoteFromBackup(parsed.mcpRemote);
|
this.restoreMcpRemoteFromBackup(parsed.mcpRemote, true);
|
||||||
this.audit("INFO", "Backup importiert (nur Einstellungen)", {
|
this.audit("INFO", "Backup importiert (nur Einstellungen)", {
|
||||||
accountSummary: buildAccountSummary(this.settings)
|
accountSummary: buildAccountSummary(this.settings)
|
||||||
});
|
});
|
||||||
@ -864,6 +867,8 @@ public async checkDebridAccounts(): Promise<DebridAccountStatus[]> {
|
|||||||
|
|
||||||
resetHistoryForRetention(this.storagePaths, this.settings.historyRetentionMode);
|
resetHistoryForRetention(this.storagePaths, this.settings.historyRetentionMode);
|
||||||
|
|
||||||
|
this.restoreMcpRemoteFromBackup(parsed.mcpRemote, false);
|
||||||
|
|
||||||
this.manager.skipShutdownPersist = true;
|
this.manager.skipShutdownPersist = true;
|
||||||
this.manager.blockAllPersistence = true;
|
this.manager.blockAllPersistence = true;
|
||||||
logger.info("Backup wiederhergestellt — App startet automatisch neu");
|
logger.info("Backup wiederhergestellt — App startet automatisch neu");
|
||||||
|
|||||||
@ -7,6 +7,7 @@ import { afterEach, describe, expect, it } from "vitest";
|
|||||||
|
|
||||||
import { buildBackupPayload, resolveMcpRemoteRestore, BackupMcpRemote } from "../src/main/backup-payload";
|
import { buildBackupPayload, resolveMcpRemoteRestore, BackupMcpRemote } from "../src/main/backup-payload";
|
||||||
import { defaultSettings } from "../src/main/constants";
|
import { defaultSettings } from "../src/main/constants";
|
||||||
|
import { normalizeSettings } from "../src/main/storage";
|
||||||
import {
|
import {
|
||||||
startDebugServer,
|
startDebugServer,
|
||||||
stopDebugServer,
|
stopDebugServer,
|
||||||
@ -100,6 +101,14 @@ describe("backup mcpRemote export gating", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("backupIncludeMcp settings persistence", () => {
|
||||||
|
it("normalizeSettings preserves backupIncludeMcp (the toggle survives save/load)", () => {
|
||||||
|
expect(normalizeSettings({ backupIncludeMcp: true } as unknown as AppSettings).backupIncludeMcp).toBe(true);
|
||||||
|
expect(normalizeSettings({ backupIncludeMcp: false } as unknown as AppSettings).backupIncludeMcp).toBe(false);
|
||||||
|
expect(normalizeSettings({} as unknown as AppSettings).backupIncludeMcp).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe("resolveMcpRemoteRestore", () => {
|
describe("resolveMcpRemoteRestore", () => {
|
||||||
it("maps network + non-empty allowlist to 0.0.0.0", () => {
|
it("maps network + non-empty allowlist to 0.0.0.0", () => {
|
||||||
expect(resolveMcpRemoteRestore({ allowlist: ["10.0.0.5"], port: 9868, hostMode: "network" }))
|
expect(resolveMcpRemoteRestore({ allowlist: ["10.0.0.5"], port: 9868, hostMode: "network" }))
|
||||||
@ -167,4 +176,24 @@ describe("backup mcpRemote live restore round-trip", () => {
|
|||||||
|
|
||||||
await waitForReady(`http://127.0.0.1:${restorePort}/health?token=rt-secret`);
|
await waitForReady(`http://127.0.0.1:${restorePort}/health?token=rt-secret`);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("full-backup path writes the debug_* files to disk without a restart (boot picks them up)", async () => {
|
||||||
|
const baseDir = fs.mkdtempSync(path.join(os.tmpdir(), "rd-bkmcp2-"));
|
||||||
|
tempDirs.push(baseDir);
|
||||||
|
const startPort = await getFreePort();
|
||||||
|
fs.writeFileSync(path.join(baseDir, "debug_token.txt"), "rt2", "utf8");
|
||||||
|
fs.writeFileSync(path.join(baseDir, "debug_port.txt"), String(startPort), "utf8");
|
||||||
|
fs.writeFileSync(path.join(baseDir, "debug_host.txt"), "127.0.0.1", "utf8");
|
||||||
|
fs.writeFileSync(path.join(baseDir, "debug_allowlist.txt"), "", "utf8");
|
||||||
|
startDebugServer({} as unknown as DownloadManager, baseDir);
|
||||||
|
await waitForReady(`http://127.0.0.1:${startPort}/health?token=rt2`);
|
||||||
|
|
||||||
|
const restore = resolveMcpRemoteRestore({ allowlist: ["198.51.100.9"], port: 9100, hostMode: "network" });
|
||||||
|
writeDebugServerConfig({ host: restore!.host, port: restore!.port, allowlist: restore!.allowlist });
|
||||||
|
|
||||||
|
expect(fs.readFileSync(path.join(baseDir, "debug_host.txt"), "utf8").trim()).toBe("0.0.0.0");
|
||||||
|
expect(fs.readFileSync(path.join(baseDir, "debug_port.txt"), "utf8").trim()).toBe("9100");
|
||||||
|
expect(fs.readFileSync(path.join(baseDir, "debug_allowlist.txt"), "utf8")).toContain("198.51.100.9");
|
||||||
|
expect(getDebugServerRuntimeStatus().port).toBe(startPort);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user