refactor: Remove unused APIs and data structures, optimize loading state component styles

This commit is contained in:
shuaiplus
2026-04-28 23:01:23 +08:00
parent 1b0386bf78
commit 69b98f9e67
18 changed files with 221 additions and 258 deletions
+3 -6
View File
@@ -1,6 +1,6 @@
import { hkdf } from '@/lib/crypto';
import { t } from '@/lib/i18n';
import type { Cipher, VaultDraft } from '@/lib/types';
import type { VaultDraft } from '@/lib/types';
import type { ImportResultSummary } from '@/components/ImportPage';
const SEND_KEY_SALT = 'bitwarden-send';
@@ -26,7 +26,7 @@ export function looksLikeCipherString(value: string): boolean {
return /^\d+\.[A-Za-z0-9+/=]+\|[A-Za-z0-9+/=]+(?:\|[A-Za-z0-9+/=]+)?$/.test(String(value || '').trim());
}
export function asText(value: unknown): string {
function asText(value: unknown): string {
if (value === null || value === undefined) return '';
return String(value);
}
@@ -106,7 +106,7 @@ export function summarizeImportResult(
};
}
export function buildEmptyImportDraft(type: number): VaultDraft {
function buildEmptyImportDraft(type: number): VaultDraft {
return {
type,
favorite: false,
@@ -279,6 +279,3 @@ export async function deriveSendKeyParts(sendKeyMaterial: Uint8Array): Promise<{
return { enc: derived.slice(0, 32), mac: derived.slice(32, 64) };
}
export function findCipherById(ciphers: Cipher[], id: string): Cipher | null {
return ciphers.find((cipher) => cipher.id === id) || null;
}
-8
View File
@@ -1,14 +1,6 @@
import { decryptStr, decryptBw } from './crypto';
import type { Cipher } from './types';
function sameBytes(a: Uint8Array, b: Uint8Array): boolean {
if (a.byteLength !== b.byteLength) return false;
for (let i = 0; i < a.byteLength; i += 1) {
if (a[i] !== b[i]) return false;
}
return true;
}
async function decryptField(
value: string | null | undefined,
enc: Uint8Array,
-79
View File
@@ -380,85 +380,6 @@ export async function buildPlainBitwardenJsonString(args: BuildPlainJsonArgs): P
return JSON.stringify(doc, null, 2);
}
export async function buildBitwardenCsvString(args: BuildPlainJsonArgs): Promise<string> {
const doc = await buildPlainBitwardenJsonDocument(args);
const folders = Array.isArray(doc.folders) ? (doc.folders as Array<Record<string, unknown>>) : [];
const items = Array.isArray(doc.items) ? (doc.items as Array<Record<string, unknown>>) : [];
const folderNameById = new Map<string, string>();
for (const folder of folders) {
const id = normalizeString(folder.id);
if (!id) continue;
folderNameById.set(id, normalizeString(folder.name) || '');
}
const header = [
'folder',
'favorite',
'type',
'name',
'notes',
'fields',
'reprompt',
'archivedDate',
'login_uri',
'login_username',
'login_password',
'login_totp',
];
const rows: string[][] = [header];
for (const item of items) {
const type = normalizeNumber(item.type, 1);
if (type !== 1 && type !== 2) continue;
const folderId = normalizeString(item.folderId);
const folderName = folderId ? folderNameById.get(folderId) || '' : '';
const fields = Array.isArray(item.fields)
? (item.fields as Array<Record<string, unknown>>)
.map((field) => {
const name = normalizeString(field.name) || '';
const value = normalizeString(field.value) || '';
if (!name && !value) return '';
return `${name}: ${value}`;
})
.filter((line) => !!line)
.join('\n')
: '';
const login = isRecord(item.login) ? (item.login as Record<string, unknown>) : null;
const loginUris = login && Array.isArray(login.uris)
? (login.uris as Array<Record<string, unknown>>)
.map((uri) => normalizeString(uri.uri) || '')
.filter((uri) => !!uri)
.join('\n')
: '';
rows.push([
folderName,
item.favorite ? '1' : '',
type === 1 ? 'login' : 'note',
normalizeString(item.name) || '',
normalizeString(item.notes) || '',
fields,
String(normalizeNumber(item.reprompt, 0)),
normalizeString(item.archivedDate) || '',
loginUris,
normalizeString(login?.username) || '',
normalizeString(login?.password) || '',
normalizeString(login?.totp) || '',
]);
}
const escapeCsv = (value: string): string => {
if (/[",\n\r]/.test(value)) {
return `"${value.replace(/"/g, '""')}"`;
}
return value;
};
return rows.map((row) => row.map((cell) => escapeCsv(String(cell || ''))).join(',')).join('\n');
}
export async function buildAccountEncryptedBitwardenJsonString(args: BuildEncryptedJsonArgs): Promise<string> {
const userEnc = base64ToBytes(args.userEncB64);
const userMac = base64ToBytes(args.userMacB64);
+2 -45
View File
@@ -1,13 +1,7 @@
import { base64ToBytes, decryptBw, decryptStr, encryptBw } from './crypto';
import { base64ToBytes, decryptBw, decryptStr } from './crypto';
import { deriveSendKeyParts } from './app-support';
import type { Cipher, Folder, Send } from './types';
export interface AttachmentRepairTask {
cipherId: string;
attachmentId: string;
metadata: { fileName?: string; key?: string | null };
}
export interface DecryptVaultCoreArgs {
folders: Folder[];
ciphers: Cipher[];
@@ -18,7 +12,6 @@ export interface DecryptVaultCoreArgs {
export interface DecryptVaultCoreResult {
folders: Folder[];
ciphers: Cipher[];
attachmentRepairs: AttachmentRepairTask[];
}
export interface DecryptSendsArgs {
@@ -28,10 +21,6 @@ export interface DecryptSendsArgs {
origin: string;
}
function looksLikeCipherString(value: string): boolean {
return /^\d+\.[A-Za-z0-9+/=]+\|[A-Za-z0-9+/=]+(?:\|[A-Za-z0-9+/=]+)?$/.test(String(value || '').trim());
}
function sameBytes(a: Uint8Array, b: Uint8Array): boolean {
if (a.byteLength !== b.byteLength) return false;
for (let i = 0; i < a.byteLength; i += 1) {
@@ -81,7 +70,6 @@ async function decryptFieldWithSource(
export async function decryptVaultCore(args: DecryptVaultCoreArgs): Promise<DecryptVaultCoreResult> {
const userEnc = base64ToBytes(args.symEncKeyB64);
const userMac = base64ToBytes(args.symMacKeyB64);
const attachmentRepairs: AttachmentRepairTask[] = [];
const folders = await Promise.all(
args.folders.map(async (folder) => ({
@@ -195,7 +183,6 @@ export async function decryptVaultCore(args: DecryptVaultCoreArgs): Promise<Decr
if (Array.isArray(cipher.attachments)) {
nextCipher.attachments = await Promise.all(
cipher.attachments.map(async (attachment) => {
const attachmentId = String(attachment?.id || '').trim();
const fileNameResult = await decryptFieldWithSource(
attachment.fileName || '',
itemEnc,
@@ -204,36 +191,6 @@ export async function decryptVaultCore(args: DecryptVaultCoreArgs): Promise<Decr
userMac,
!itemUsesUserKey
);
const metadata: { fileName?: string; key?: string | null } = {};
if (attachmentId && fileNameResult.source === 'user') {
metadata.fileName = await encryptBw(new TextEncoder().encode(fileNameResult.text), itemEnc, itemMac);
}
const attachmentKey = String(attachment?.key || '').trim();
if (attachmentId && attachmentKey && looksLikeCipherString(attachmentKey) && !itemUsesUserKey) {
try {
await decryptBw(attachmentKey, itemEnc, itemMac);
} catch {
try {
const rawAttachmentKey = await decryptBw(attachmentKey, userEnc, userMac);
if (rawAttachmentKey.length >= 64) {
metadata.key = await encryptBw(rawAttachmentKey, itemEnc, itemMac);
}
} catch {
// Download path still supports legacy format.
}
}
}
if (attachmentId && Object.keys(metadata).length > 0) {
attachmentRepairs.push({
cipherId: cipher.id,
attachmentId,
metadata,
});
}
return {
...attachment,
decFileName: fileNameResult.text,
@@ -246,7 +203,7 @@ export async function decryptVaultCore(args: DecryptVaultCoreArgs): Promise<Decr
})
);
return { folders, ciphers, attachmentRepairs };
return { folders, ciphers };
}
export async function decryptSends(args: DecryptSendsArgs): Promise<Send[]> {