Require canonical encrypted envelopes and surface typed sanitized keyring corruption states. Persist crash-durable primary and recovery files, prevalidate removal plans, isolate renderer refresh authority, and cover the real hidden Windows DPAPI and IPC composition.
151 lines
4.8 KiB
JavaScript
151 lines
4.8 KiB
JavaScript
const {
|
|
createOnlineBackup,
|
|
deleteOnlineBackup,
|
|
uploadOnlineBackup
|
|
} = require('./online-backup');
|
|
const { KEYRING_ERROR_CODES } = require('./online-backup-keyring');
|
|
|
|
const ERRORS = Object.freeze({
|
|
list: 'Online-Sicherungen konnten nicht geladen werden',
|
|
create: 'Online-Sicherung konnte nicht erstellt werden',
|
|
copy: 'Online-Sicherungsschlüssel konnte nicht kopiert werden',
|
|
delete: 'Online-Sicherung konnte nicht gelöscht werden',
|
|
notFound: 'Online-Sicherungsschlüssel wurde nicht gefunden'
|
|
});
|
|
const KEYRING_MESSAGES = Object.freeze({
|
|
[KEYRING_ERROR_CODES.structure]: 'Gespeicherter Online-Schlüsselbund ist beschädigt',
|
|
[KEYRING_ERROR_CODES.unavailable]: 'Sichere Schlüsselspeicherung ist nicht verfügbar',
|
|
[KEYRING_ERROR_CODES.decrypt]: 'Gespeicherter Online-Sicherungsschlüssel konnte nicht entschlüsselt werden',
|
|
[KEYRING_ERROR_CODES.mismatch]: 'Gespeicherte Online-Sicherungskennung stimmt nicht mit dem Schlüssel überein',
|
|
[KEYRING_ERROR_CODES.duplicate]: 'Gespeicherte Online-Sicherungskennung ist mehrdeutig',
|
|
[KEYRING_ERROR_CODES.recovered]: 'Online-Schlüsselbund wurde aus einer Wiederherstellungsdatei geladen',
|
|
[KEYRING_ERROR_CODES.encrypt]: 'Online-Sicherungsschlüssel konnte nicht sicher vorbereitet werden',
|
|
[KEYRING_ERROR_CODES.plan]: 'Online-Sicherung konnte lokal nicht eindeutig entfernt werden'
|
|
});
|
|
|
|
function sanitizeEntry(entry) {
|
|
return {
|
|
id: entry.id,
|
|
displayKey: entry.displayKey,
|
|
createdAt: entry.createdAt
|
|
};
|
|
}
|
|
|
|
function sanitizeCreatedEntry(entry, key) {
|
|
return {
|
|
id: entry.id,
|
|
displayKey: `${key.slice(0, 9)}…${key.slice(-4)}`,
|
|
createdAt: entry.createdAt
|
|
};
|
|
}
|
|
|
|
function keyringFailure(error, fallback) {
|
|
const message = KEYRING_MESSAGES[error?.code];
|
|
if (!message) return { ok: false, error: fallback };
|
|
return { ok: false, code: error.code, error: message };
|
|
}
|
|
|
|
function createOnlineBackupManager({
|
|
keyring,
|
|
loadSettings,
|
|
appVersion,
|
|
createBackup = createOnlineBackup,
|
|
uploadBackup = uploadOnlineBackup,
|
|
deleteBackup = deleteOnlineBackup,
|
|
copyText
|
|
}) {
|
|
let mutation = Promise.resolve();
|
|
|
|
function serialize(operation) {
|
|
const next = mutation.catch(() => {}).then(operation);
|
|
mutation = next;
|
|
return next;
|
|
}
|
|
|
|
async function listEntries() {
|
|
const snapshot = await keyring.list();
|
|
if (!snapshot || !Array.isArray(snapshot.entries) || !Array.isArray(snapshot.issues)) throw new Error(ERRORS.list);
|
|
return {
|
|
entries: snapshot.entries.map(sanitizeEntry),
|
|
issues: snapshot.issues
|
|
};
|
|
}
|
|
|
|
async function createTransaction() {
|
|
const createdAt = new Date().toISOString();
|
|
const settings = await loadSettings();
|
|
const created = createBackup(settings, appVersion(), createdAt);
|
|
const prepared = keyring.prepare(created.key, createdAt);
|
|
await uploadBackup(created.record);
|
|
try {
|
|
await keyring.commit(prepared);
|
|
} catch (error) {
|
|
try {
|
|
await deleteBackup(created.key);
|
|
} catch {}
|
|
throw error;
|
|
}
|
|
return { ok: true, entry: sanitizeCreatedEntry(prepared, created.key) };
|
|
}
|
|
|
|
async function deleteTransaction(id) {
|
|
const plan = await keyring.prepareRemove(id);
|
|
if (!plan) return { ok: false, notFound: true, error: ERRORS.notFound };
|
|
const outcome = await deleteBackup(plan.key);
|
|
if (!outcome?.deleted && !outcome?.notFound) throw new Error(ERRORS.delete);
|
|
await keyring.commitRemove(plan);
|
|
return { ok: true, removedId: id, notFound: outcome.notFound };
|
|
}
|
|
|
|
async function listManaged() {
|
|
try {
|
|
const snapshot = await listEntries();
|
|
const issue = snapshot.issues[0];
|
|
if (!issue) return { ok: true, entries: snapshot.entries };
|
|
const message = KEYRING_MESSAGES[issue] || ERRORS.list;
|
|
if (snapshot.entries.length || issue === KEYRING_ERROR_CODES.recovered) {
|
|
return {
|
|
ok: true,
|
|
entries: snapshot.entries,
|
|
warningCode: issue,
|
|
warning: message
|
|
};
|
|
}
|
|
return { ok: false, entries: [], code: issue, error: message };
|
|
} catch (error) {
|
|
return keyringFailure(error, ERRORS.list);
|
|
}
|
|
}
|
|
|
|
async function createManaged() {
|
|
try {
|
|
return await serialize(createTransaction);
|
|
} catch (error) {
|
|
return keyringFailure(error, ERRORS.create);
|
|
}
|
|
}
|
|
|
|
async function copyManaged(id) {
|
|
try {
|
|
const key = await keyring.getKey(id);
|
|
if (!key) return { ok: false, notFound: true, error: ERRORS.notFound };
|
|
await copyText(key);
|
|
return { ok: true };
|
|
} catch (error) {
|
|
return keyringFailure(error, ERRORS.copy);
|
|
}
|
|
}
|
|
|
|
async function deleteManaged(id) {
|
|
try {
|
|
return await serialize(() => deleteTransaction(id));
|
|
} catch (error) {
|
|
return keyringFailure(error, ERRORS.delete);
|
|
}
|
|
}
|
|
|
|
return Object.freeze({ listManaged, createManaged, copyManaged, deleteManaged });
|
|
}
|
|
|
|
module.exports = { createOnlineBackupManager };
|