From 40fe9223acf55c23e9c1c9b23f93cd0d70faa31c Mon Sep 17 00:00:00 2001 From: shuaiplus <2327005759@qq.com> Date: Tue, 17 Mar 2026 09:03:14 +0800 Subject: [PATCH] feat: add parseSerializedUris function and update Bitwarden CSV parsing to handle multiple URIs --- webapp/src/lib/export-formats.ts | 2 +- webapp/src/lib/import-format-shared.ts | 30 ++++++++++++++++++++++++ webapp/src/lib/import-formats-browser.ts | 6 ++--- 3 files changed, 34 insertions(+), 4 deletions(-) diff --git a/webapp/src/lib/export-formats.ts b/webapp/src/lib/export-formats.ts index a218d83..87bf194 100644 --- a/webapp/src/lib/export-formats.ts +++ b/webapp/src/lib/export-formats.ts @@ -428,7 +428,7 @@ export async function buildBitwardenCsvString(args: BuildPlainJsonArgs): Promise ? (login.uris as Array>) .map((uri) => normalizeString(uri.uri) || '') .filter((uri) => !!uri) - .join(',') + .join('\n') : ''; rows.push([ diff --git a/webapp/src/lib/import-format-shared.ts b/webapp/src/lib/import-format-shared.ts index 288042b..3da7ac1 100644 --- a/webapp/src/lib/import-format-shared.ts +++ b/webapp/src/lib/import-format-shared.ts @@ -19,6 +19,36 @@ export function normalizeUri(raw: string): string | null { return s.slice(0, 1000); } +export function parseSerializedUris(raw: string): string[] { + const source = txt(raw); + if (!source) return []; + + const newlineParts = source + .split(/\r?\n/) + .map((part) => txt(part)) + .filter(Boolean); + + const parts = + newlineParts.length > 1 + ? newlineParts + : source.includes(',') + ? source + .split(/,(?=\s*(?:[a-z][a-z0-9+.-]*:\/\/|www\.|[a-z0-9.-]+\.[a-z]{2,}(?:[/:?#]|$)))/i) + .map((part) => txt(part)) + .filter(Boolean) + : [source]; + + const seen = new Set(); + const uris: string[] = []; + for (const part of parts) { + const normalized = normalizeUri(part); + if (!normalized || seen.has(normalized)) continue; + seen.add(normalized); + uris.push(normalized); + } + return uris; +} + export function nameFromUrl(raw: string): string | null { const uri = normalizeUri(raw); if (!uri) return null; diff --git a/webapp/src/lib/import-formats-browser.ts b/webapp/src/lib/import-formats-browser.ts index 5f33e1d..5dead49 100644 --- a/webapp/src/lib/import-formats-browser.ts +++ b/webapp/src/lib/import-formats-browser.ts @@ -1,5 +1,5 @@ import type { CiphersImportPayload } from '@/lib/api/vault'; -import { addFolder, cardBrand, makeLoginCipher, nameFromUrl, normalizeUri, parseCsv, txt, val } from '@/lib/import-format-shared'; +import { addFolder, cardBrand, makeLoginCipher, nameFromUrl, normalizeUri, parseCsv, parseSerializedUris, txt, val } from '@/lib/import-format-shared'; export function parseChromeCsv(textRaw: string): CiphersImportPayload { const rows = parseCsv(textRaw); @@ -92,8 +92,8 @@ export function parseBitwardenCsv(textRaw: string): CiphersImportPayload { login.username = val(row.login_username); login.password = val(row.login_password); login.totp = val(row.login_totp); - const uri = normalizeUri(row.login_uri || ''); - login.uris = uri ? [{ uri, match: null }] : null; + const uris = parseSerializedUris(row.login_uri || ''); + login.uris = uris.length ? uris.map((uri) => ({ uri, match: null })) : null; const idx = result.ciphers.push(cipher) - 1; addFolder(result, row.folder, idx); }