mirror of
https://github.com/bulwarkmail/webmail.git
synced 2026-11-04 16:14:07 +00:00
Until now a lost laptop kept its offline mail for as long as anyone could open it, and an account whose login the server had ended kept its copy too. - `lib/devices`, `app/api/devices`: browsers with offline on and desktop apps register, in a durable collection with no expiry. A check-in goes by a per-device secret, so a revoked device still hears its wipe. - `app/api/admin/devices` and the org devices page: wipe one device or all of a user's, with audit entries for each request and each confirmation. - `lib/offline/device-wipe.ts`: a start finishes any wipe cut short, then waits up to a second for the instance before showing the local copy. A wipe takes the store, keys, plugin databases and handoffs, signs out, and reports back, again at the next contact if the report failed. - `lib/jmap/client.ts`, `stores/auth-store.ts`: only `invalid_grant`, the session limit, or a 401 lasting past a re-auth count as refused. A 500, a proxy page or an outage never do. - Settings: "Remove everything from this device", with a confirmation. `lib/shared-store/__tests__/import-data.test.ts` changes its assertion because it lists every collection the import copies, and `devices` is new.
45 lines
1.9 KiB
TypeScript
45 lines
1.9 KiB
TypeScript
import { getStore } from '@/lib/shared-store';
|
|
import { localCollection } from '@/lib/shared-store/local';
|
|
import type { CollectionOptions } from '@/lib/shared-store/types';
|
|
import type { ImportStep } from '@/lib/shared-store/import';
|
|
import { SETTINGS_COLLECTION } from '@/lib/settings-sync';
|
|
import { VAULT_COLLECTION } from '@/lib/plugin-secrets/vault';
|
|
import { ARCHIVE_COLLECTION } from '@/lib/account-archive-storage';
|
|
import { TELEMETRY_COLLECTION } from '@/lib/telemetry/state';
|
|
import { VERSION_CHECK_COLLECTION } from '@/lib/version-check/state';
|
|
import { DEVICES_COLLECTION } from '@/lib/devices/registry';
|
|
|
|
/**
|
|
* Part `data` of the shared-store import (#1066): every document of these
|
|
* collections, read from the local files exactly as local mode reads them,
|
|
* written to the shared store only where it holds nothing yet. The bytes go
|
|
* over as they are: settings and plugin credentials stay encrypted with the
|
|
* session secret, which the boot imports first.
|
|
*/
|
|
function copyCollection(name: string, options: CollectionOptions): ImportStep {
|
|
return {
|
|
name,
|
|
async run() {
|
|
const local = localCollection(name, options);
|
|
const shared = getStore().collection(name, options);
|
|
let written = 0;
|
|
for (const id of await local.list()) {
|
|
const doc = await local.get(id);
|
|
if (!doc) continue;
|
|
if ((await shared.put(id, doc.data, { ifVersion: null })).ok) written++;
|
|
}
|
|
return written;
|
|
},
|
|
};
|
|
}
|
|
|
|
export const steps: ImportStep[] = [
|
|
copyCollection('settings', SETTINGS_COLLECTION),
|
|
copyCollection('plugin-secrets', VAULT_COLLECTION),
|
|
copyCollection('account-archives', ARCHIVE_COLLECTION),
|
|
// state.json, logins.json and the instance id .telemetry-id
|
|
copyCollection('telemetry', TELEMETRY_COLLECTION),
|
|
copyCollection('version-check', VERSION_CHECK_COLLECTION),
|
|
copyCollection('devices', DEVICES_COLLECTION),
|
|
];
|