Skip to content

Commit 24f18c0

Browse files
VishalJ99openclaw-clownfish[bot]
authored andcommitted
fix: preserve WhatsApp backup during phone-code cleanup
1 parent 485e16f commit 24f18c0

2 files changed

Lines changed: 74 additions & 7 deletions

File tree

extensions/whatsapp/src/auth-store.test.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,46 @@ describe("auth-store", () => {
294294
});
295295
});
296296

297+
it("restores valid backup before clearing partial phone-code pairing credentials", async () => {
298+
await withOwnedOAuthAuthDir("openclaw-wa-auth-clear-pairing-with-backup", async (authDir) => {
299+
const credsPath = path.join(authDir, "creds.json");
300+
const backupPath = path.join(authDir, "creds.json.bak");
301+
const backupCreds = {
302+
account: { details: "present" },
303+
me: { id: "[email protected]" },
304+
platform: "chrome",
305+
registered: false,
306+
};
307+
fsSync.writeFileSync(
308+
credsPath,
309+
JSON.stringify({
310+
me: { id: "[email protected]" },
311+
pairingCode: "ABCDEFGH",
312+
registered: false,
313+
}),
314+
"utf-8",
315+
);
316+
fsSync.writeFileSync(backupPath, JSON.stringify(backupCreds), "utf-8");
317+
const runtime = {
318+
log: vi.fn(),
319+
error: vi.fn(),
320+
exit: vi.fn(),
321+
};
322+
323+
await expect(
324+
clearStalePhoneCodePairingAuthIfNeeded({ authDir, runtime: runtime as never }),
325+
).resolves.toBe(true);
326+
327+
expect(fsSync.existsSync(authDir)).toBe(true);
328+
expect(JSON.parse(fsSync.readFileSync(credsPath, "utf-8"))).toEqual(backupCreds);
329+
expect(JSON.parse(fsSync.readFileSync(backupPath, "utf-8"))).toEqual(backupCreds);
330+
expect(hasWebCredsSync(authDir)).toBe(true);
331+
expect(runtime.log).toHaveBeenCalledWith(
332+
expect.stringContaining("Restored WhatsApp Web credentials from backup"),
333+
);
334+
});
335+
});
336+
297337
it("keeps fully linked credentials during partial-pairing cleanup", async () => {
298338
await withOwnedOAuthAuthDir("openclaw-wa-auth-keep-linked", async (authDir) => {
299339
fsSync.writeFileSync(

extensions/whatsapp/src/auth-store.ts

Lines changed: 34 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -92,15 +92,9 @@ export async function restoreCredsFromBackupIfNeeded(authDir: string): Promise<b
9292
return false;
9393
}
9494

95-
const backupRaw = readCredsJsonRaw(backupPath);
96-
if (!backupRaw || !isValidJson(backupRaw)) {
95+
if (!(await restoreWebCredsFromBackupRaw({ credsPath, backupPath }))) {
9796
return false;
9897
}
99-
await writeWebCredsRawAtomically({
100-
filePath: credsPath,
101-
content: backupRaw,
102-
tempPrefix: ".creds.restore",
103-
});
10498
logger.warn({ credsPath }, "restored corrupted WhatsApp creds.json from backup");
10599
return true;
106100
} catch {
@@ -109,6 +103,25 @@ export async function restoreCredsFromBackupIfNeeded(authDir: string): Promise<b
109103
return false;
110104
}
111105

106+
async function restoreWebCredsFromBackupRaw(params: {
107+
backupPath: string;
108+
credsPath: string;
109+
}): Promise<boolean> {
110+
const backupRaw = readCredsJsonRaw(params.backupPath);
111+
if (!backupRaw) {
112+
return false;
113+
}
114+
115+
// Ensure backup is parseable before restoring.
116+
JSON.parse(backupRaw);
117+
await writeWebCredsRawAtomically({
118+
filePath: params.credsPath,
119+
content: backupRaw,
120+
tempPrefix: ".creds.restore",
121+
});
122+
return true;
123+
}
124+
112125
async function clearOwnedWebAuthDir(params: {
113126
authDir: string;
114127
isLegacyAuthDir: boolean;
@@ -178,6 +191,20 @@ export async function clearStalePhoneCodePairingAuthIfNeeded(params: {
178191
return false;
179192
}
180193

194+
if (
195+
await restoreWebCredsFromBackupRaw({
196+
credsPath: resolveWebCredsPath(resolvedAuthDir),
197+
backupPath: resolveWebCredsBackupPath(resolvedAuthDir),
198+
}).catch(() => false)
199+
) {
200+
runtime.log(
201+
info(
202+
"Restored WhatsApp Web credentials from backup instead of clearing partial phone-code auth.",
203+
),
204+
);
205+
return true;
206+
}
207+
181208
const cleared = await clearOwnedWebAuthDir({
182209
authDir: resolvedAuthDir,
183210
isLegacyAuthDir: Boolean(params.isLegacyAuthDir),

0 commit comments

Comments
 (0)