feat(ext/sw): restore_backup handler
Unpacks .relbak via WASM, writes every vault artifact to the user-specified fresh remote via writeFileCreateOnly (refuses to clobber), and updates chrome.storage.local so subsequent unlocks hit the restored vault. The reference image — when bundled — is restored to imageBase64; otherwise the user keeps using their existing reference.jpg.
This commit is contained in:
@@ -418,8 +418,94 @@ export async function handle(
|
||||
}
|
||||
}
|
||||
|
||||
case 'restore_backup':
|
||||
return { ok: false, error: 'restore_backup not yet implemented' };
|
||||
case 'restore_backup': {
|
||||
try {
|
||||
const bytes = new Uint8Array(msg.bytes);
|
||||
const outJson: string = state.wasm.unpack_backup_json(bytes, msg.passphrase);
|
||||
const out = JSON.parse(outJson) as {
|
||||
salt: string;
|
||||
params_json: string;
|
||||
devices_json: string;
|
||||
manifest_enc: string;
|
||||
settings_enc: string;
|
||||
items: Array<{ id: string; ciphertext: string }>;
|
||||
attachments: Array<{ item_id: string; attachment_id: string; ciphertext: string }>;
|
||||
reference_jpg: string | null;
|
||||
};
|
||||
|
||||
// Build a GitHost for the new remote.
|
||||
const newHost = createGitHost(
|
||||
msg.newRemote.hostType,
|
||||
msg.newRemote.hostUrl,
|
||||
msg.newRemote.repoPath,
|
||||
msg.newRemote.apiToken,
|
||||
);
|
||||
|
||||
// Refuse if the remote already has a vault.
|
||||
try {
|
||||
const meta = await vault.fetchVaultMeta(newHost);
|
||||
if (meta.salt && meta.paramsJson) {
|
||||
return { ok: false, error: 'remote already contains a relicario vault' };
|
||||
}
|
||||
} catch {
|
||||
// No vault present — expected for a fresh remote.
|
||||
}
|
||||
|
||||
// Write the layout via writeFileCreateOnly. Refuses to clobber.
|
||||
const b64 = (s: string) => Uint8Array.from(atob(s), c => c.charCodeAt(0));
|
||||
await newHost.writeFileCreateOnly('.relicario/salt', b64(out.salt), 'restore: salt');
|
||||
await newHost.writeFileCreateOnly('.relicario/params.json', new TextEncoder().encode(out.params_json), 'restore: params.json');
|
||||
await newHost.writeFileCreateOnly('.relicario/devices.json', new TextEncoder().encode(out.devices_json), 'restore: devices.json');
|
||||
await newHost.writeFileCreateOnly('manifest.enc', b64(out.manifest_enc), 'restore: manifest.enc');
|
||||
await newHost.writeFileCreateOnly('settings.enc', b64(out.settings_enc), 'restore: settings.enc');
|
||||
|
||||
for (const it of out.items) {
|
||||
await newHost.writeFileCreateOnly(
|
||||
`items/${it.id}.enc`, b64(it.ciphertext), `restore: item ${it.id}`,
|
||||
);
|
||||
}
|
||||
// Translate canonical envelope keys (<item_id>/<aid>) back to the
|
||||
// extension's flat layout (attachments/<aid>.bin). The aid is
|
||||
// already content-addressed and globally unique; the item_id segment
|
||||
// is recorded only in the manifest's attachment_summaries.
|
||||
for (const a of out.attachments) {
|
||||
await newHost.writeFileCreateOnly(
|
||||
`attachments/${a.attachment_id}.bin`,
|
||||
b64(a.ciphertext),
|
||||
`restore: attachment ${a.attachment_id}`,
|
||||
);
|
||||
}
|
||||
|
||||
// Update local config so subsequent unlocks work.
|
||||
const cfg = {
|
||||
hostType: msg.newRemote.hostType,
|
||||
hostUrl: msg.newRemote.hostUrl,
|
||||
repoPath: msg.newRemote.repoPath,
|
||||
apiToken: msg.newRemote.apiToken,
|
||||
};
|
||||
await chrome.storage.local.set({ vaultConfig: cfg });
|
||||
if (out.reference_jpg) {
|
||||
await chrome.storage.local.set({ imageBase64: out.reference_jpg });
|
||||
}
|
||||
|
||||
// Make sure the SW's gitHost cache picks up the new config.
|
||||
state.gitHost = newHost;
|
||||
state.manifest = null; // user must unlock to populate
|
||||
|
||||
return {
|
||||
ok: true,
|
||||
data: {
|
||||
summary: {
|
||||
itemCount: out.items.length,
|
||||
attachmentCount: out.attachments.length,
|
||||
hasImage: out.reference_jpg != null,
|
||||
},
|
||||
},
|
||||
};
|
||||
} catch (e) {
|
||||
return { ok: false, error: (e as Error).message };
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user