Files
nodewarden/webapp/src/lib/import-formats-bitwarden.ts
T
shuaiplus 76623d7201 Refactor: Remove passkey-related functionality and types
- Deleted passkey-related interfaces and types from index.ts and types.ts.
- Removed passkey handling from App component, including related state and functions.
- Cleaned up API calls in auth.ts, removing passkey registration and login functions.
- Updated vault and import formats to eliminate passkey references.
- Removed passkey support checks and UI elements from AuthViews and SettingsPage.
- Cleaned up unused passkey helper functions and constants.
- Adjusted related components and hooks to ensure consistent functionality without passkey support.
2026-04-06 00:46:13 +08:00

156 lines
5.4 KiB
TypeScript

import type { CiphersImportPayload } from '@/lib/api/vault';
export interface BitwardenFolderInput {
id?: string | null;
name?: string | null;
}
export interface BitwardenUriInput {
uri?: string | null;
match?: number | null;
}
export interface BitwardenFieldInput {
name?: string | null;
value?: string | null;
type?: number | null;
linkedId?: number | null;
}
export interface BitwardenCipherInput {
id?: string | null;
type?: number | null;
name?: string | null;
notes?: string | null;
favorite?: boolean | null;
reprompt?: number | null;
key?: string | null;
folderId?: string | null;
login?: {
uris?: BitwardenUriInput[] | null;
username?: string | null;
password?: string | null;
totp?: string | null;
} | null;
card?: Record<string, unknown> | null;
identity?: Record<string, unknown> | null;
secureNote?: { type?: number | null } | null;
fields?: BitwardenFieldInput[] | null;
passwordHistory?: Array<{ password?: string | null; lastUsedDate?: string | null }> | null;
sshKey?: Record<string, unknown> | null;
}
export interface BitwardenJsonInput {
encrypted?: boolean;
passwordProtected?: boolean;
encKeyValidation_DO_NOT_EDIT?: string;
collections?: Array<{ id?: string | null; name?: string | null }> | null;
folders?: BitwardenFolderInput[] | null;
items?: BitwardenCipherInput[] | null;
}
function txt(v: unknown): string {
if (v === null || v === undefined) return '';
return String(v).trim();
}
export function normalizeBitwardenImport(raw: unknown): CiphersImportPayload {
const parsed = raw as BitwardenJsonInput | null;
if (!parsed || typeof parsed !== 'object') throw new Error('Invalid Bitwarden JSON');
if (parsed.encrypted === true) throw new Error('Encrypted export requires encrypted import flow.');
const foldersRaw = Array.isArray(parsed.folders) ? parsed.folders : [];
const itemsRaw = Array.isArray(parsed.items) ? parsed.items : [];
const folders: Array<{ name: string }> = [];
const folderIndexById = new Map<string, number>();
for (const folder of foldersRaw) {
const name = txt(folder?.name);
if (!name) continue;
const idx = folders.length;
folders.push({ name });
const id = txt(folder?.id);
if (id) folderIndexById.set(id, idx);
}
const ciphers: Array<Record<string, unknown>> = [];
const folderRelationships: Array<{ key: number; value: number }> = [];
let hasAnyExplicitFolderLink = false;
for (const item of itemsRaw) {
ciphers.push({
id: item?.id ?? null,
type: Number(item?.type || 1) || 1,
name: item?.name ?? 'Untitled',
notes: item?.notes ?? null,
favorite: !!item?.favorite,
reprompt: Number(item?.reprompt ?? 0) || 0,
key: item?.key ?? null,
login: item?.login
? {
username: item.login.username ?? null,
password: item.login.password ?? null,
totp: item.login.totp ?? null,
uris: Array.isArray(item.login.uris)
? item.login.uris.map((u) => ({ uri: u?.uri ?? null, match: u?.match ?? null }))
: null,
}
: null,
card: item?.card ?? null,
identity: item?.identity ?? null,
secureNote: item?.secureNote ?? null,
fields: Array.isArray(item?.fields)
? item.fields.map((f) => ({
name: f?.name ?? null,
value: f?.value ?? null,
type: Number(f?.type ?? 0) || 0,
linkedId: f?.linkedId ?? null,
}))
: null,
passwordHistory: Array.isArray(item?.passwordHistory)
? item.passwordHistory
.map((x) => ({ password: x?.password ?? null, lastUsedDate: x?.lastUsedDate ?? null }))
.filter((x) => !!x.password)
: null,
sshKey: item?.sshKey ?? null,
});
const folderId = txt(item?.folderId);
if (!folderId) continue;
const folderIndex = folderIndexById.get(folderId);
if (folderIndex !== undefined) {
hasAnyExplicitFolderLink = true;
folderRelationships.push({ key: ciphers.length - 1, value: folderIndex });
}
}
if (!hasAnyExplicitFolderLink && folders.length === 1 && ciphers.length > 0) {
for (let i = 0; i < ciphers.length; i++) {
folderRelationships.push({ key: i, value: 0 });
}
}
return { ciphers, folders, folderRelationships };
}
export function normalizeBitwardenEncryptedAccountImport(raw: BitwardenJsonInput): CiphersImportPayload {
const itemsRaw = Array.isArray(raw.items) ? raw.items : [];
const foldersRaw = Array.isArray(raw.folders) ? raw.folders : [];
if (!Array.isArray(raw.folders) && Array.isArray(raw.collections)) {
throw new Error('Encrypted organization export is not supported yet.');
}
const folders = foldersRaw.map((f) => ({ name: String(f?.name ?? '') }));
const folderIndexByLegacyId = new Map<string, number>();
for (let i = 0; i < foldersRaw.length; i++) {
const folderId = txt(foldersRaw[i]?.id);
if (folderId) folderIndexByLegacyId.set(folderId, i);
}
const ciphers = itemsRaw.map((x) => ({ ...(x as Record<string, unknown>) }));
const folderRelationships: Array<{ key: number; value: number }> = [];
for (let i = 0; i < itemsRaw.length; i++) {
const folderId = txt(itemsRaw[i]?.folderId);
if (!folderId) continue;
const folderIndex = folderIndexByLegacyId.get(folderId);
if (folderIndex !== undefined) folderRelationships.push({ key: i, value: folderIndex });
}
return { ciphers, folders, folderRelationships };
}