From 2fe29ed60437b3318f039e909a3015fb5feb74c0 Mon Sep 17 00:00:00 2001 From: oyi77 Date: Sat, 9 May 2026 15:55:07 +0700 Subject: [PATCH 1/2] feat(providers): batch delete provider connections via checkbox multi-select - Add deleteProviderConnections(ids: string[]) to DB layer with 100-id cap - Add DELETE /api/providers endpoint accepting {ids: string[]} - Add UI: checkbox per connection row, select-all header bar, batch delete toolbar - Reuse singular delete snapshot/validation pattern for consistency - 16 new tests (10 DB CRUD + 6 API route), all passing - Add batchDeleteSelected/batchDeleteConfirm/batchDeleteSuccess i18n keys across all 41 locales - Fix ConnectionRowProps duplicate props and indentation in ConnectionRow calls - Closes #2093 --- .../feat-batch-delete-provider-accounts.md | 328 ++++++++++++++++++ .../dashboard/providers/[id]/page.tsx | 262 +++++++++----- src/app/api/providers/route.ts | 53 +++ src/i18n/messages/ar.json | 5 +- src/i18n/messages/bg.json | 5 +- src/i18n/messages/bn.json | 3 + src/i18n/messages/cs.json | 5 +- src/i18n/messages/da.json | 5 +- src/i18n/messages/de.json | 5 +- src/i18n/messages/en.json | 3 + src/i18n/messages/es.json | 5 +- src/i18n/messages/fa.json | 3 + src/i18n/messages/fi.json | 5 +- src/i18n/messages/fr.json | 5 +- src/i18n/messages/gu.json | 3 + src/i18n/messages/he.json | 5 +- src/i18n/messages/hi.json | 5 +- src/i18n/messages/hu.json | 5 +- src/i18n/messages/id.json | 5 +- src/i18n/messages/in.json | 3 + src/i18n/messages/it.json | 5 +- src/i18n/messages/ja.json | 5 +- src/i18n/messages/ko.json | 5 +- src/i18n/messages/mr.json | 3 + src/i18n/messages/ms.json | 5 +- src/i18n/messages/nl.json | 5 +- src/i18n/messages/no.json | 5 +- src/i18n/messages/phi.json | 5 +- src/i18n/messages/pl.json | 5 +- src/i18n/messages/pt-BR.json | 5 +- src/i18n/messages/pt.json | 5 +- src/i18n/messages/ro.json | 5 +- src/i18n/messages/ru.json | 5 +- src/i18n/messages/sk.json | 5 +- src/i18n/messages/sv.json | 5 +- src/i18n/messages/sw.json | 3 + src/i18n/messages/ta.json | 3 + src/i18n/messages/te.json | 3 + src/i18n/messages/th.json | 5 +- src/i18n/messages/tr.json | 5 +- src/i18n/messages/uk-UA.json | 5 +- src/i18n/messages/ur.json | 3 + src/i18n/messages/vi.json | 5 +- src/i18n/messages/zh-CN.json | 5 +- src/lib/db/providers.ts | 18 + src/lib/localDb.ts | 1 + src/models/index.ts | 1 + tests/unit/db-providers-crud.test.ts | 34 ++ .../providers-route-managed-catalog.test.ts | 69 ++++ 49 files changed, 837 insertions(+), 114 deletions(-) create mode 100644 .issues/feat-batch-delete-provider-accounts.md diff --git a/.issues/feat-batch-delete-provider-accounts.md b/.issues/feat-batch-delete-provider-accounts.md new file mode 100644 index 00000000000..fe84a123c80 --- /dev/null +++ b/.issues/feat-batch-delete-provider-accounts.md @@ -0,0 +1,328 @@ +# Feature Proposal: Batch Delete Provider Accounts + +## Summary + +Add **batch delete** functionality for provider accounts (connections) in the provider detail page (`/dashboard/providers/[id]`). Users select multiple accounts via checkboxes and delete them in a single action, replacing the current one-by-one delete workflow. + +## Problem Statement + +Users managing multiple provider accounts (e.g., 20+ API keys or OAuth connections) have to delete accounts individually. Each deletion requires: + +1. Finding the account +2. Clicking the delete button +3. Confirming via browser `confirm()` dialog +4. Waiting for the API call +5. Repeating for every account + +This is: +- **Time-consuming**: O(n) confirm dialogs and API calls for n accounts +- **Error-prone**: Easy to accidentally click the wrong account +- **Tedious**: No way to quickly clean up stale or duplicate accounts + +## Solution + +Add a checkbox-based selection UI to the provider connections list: + +``` +┌────────────────────────────────────────────────────────────────┐ +│ [x] Account #1 (kiro-prod) [Delete Selected (3)] │ +│ [x] Account #2 (kiro-staging) │ +│ [ ] Account #3 (kiro-backup) │ +└────────────────────────────────────────────────────────────────┘ +``` + +## Detailed PR Specification + +### Files to Modify + +| File | Change | +|------|--------| +| `src/app/(dashboard)/dashboard/providers/[id]/page.tsx` | Add batch delete state, select-all + per-row checkboxes, batch delete handler | +| `src/app/api/providers/route.ts` | Add `DELETE /api/providers` with `POST` body `ids: string[]` for batch delete | +| `src/lib/db/providers.ts` | Add `deleteProviderConnections(ids: string[])` batch DB function | +| `src/i18n/messages/en.json` | Add i18n keys: `batchDeleteSelected`, `batchDeleteConfirm`, `batchDeleteSuccess` | +| `src/i18n/messages/*.json` | Add i18n keys to all locale files | +| `tests/unit/db-providers-crud.test.ts` | Add unit tests for batch delete DB function | +| `tests/integration/api-routes-critical.test.ts` | Add integration test for batch delete API endpoint | + +### 1. DB Layer (`src/lib/db/providers.ts`) + +```typescript +export async function deleteProviderConnections(ids: string[]): Promise { + const db = getDbInstance() as unknown as DbLike; + if (ids.length === 0) return 0; + + // Delete quota snapshots for each connection first + const deleteSnapshots = db.prepare("DELETE FROM quota_snapshots WHERE connection_id = ?"); + for (const id of ids) { + deleteSnapshots.run(id); + } + + // Batch delete connections + const placeholders = ids.map(() => "?").join(","); + const result = db.prepare( + `DELETE FROM provider_connections WHERE id IN (${placeholders})` + ).run(...ids); + + backupDbFile("pre-write"); + invalidateDbCache("connections"); + return result.changes ?? 0; +} +``` + +### 2. API Route (`src/app/api/providers/route.ts`) + +Add a new route handler for batch delete. The existing `/api/providers/[id]` only handles single-id operations. The main providers route file (`/api/providers/route.ts`) should be extended: + +```typescript +// DELETE /api/providers — Batch delete connections +// Body: { ids: string[] } +export async function DELETE(request: Request) { + const authError = await requireManagementAuth(request); + if (authError) return authError; + + let body: { ids?: string[] }; + try { + body = await request.json(); + } catch { + return NextResponse.json({ error: "Invalid JSON body" }, { status: 400 }); + } + + if (!Array.isArray(body.ids) || body.ids.length === 0) { + return NextResponse.json( + { error: "ids must be a non-empty array of connection IDs" }, + { status: 400 } + ); + } + + if (body.ids.length > 100) { + return NextResponse.json( + { error: "Cannot delete more than 100 connections at once" }, + { status: 400 } + ); + } + + try { + const deleted = await deleteProviderConnections(body.ids); + await syncToCloudIfEnabled(); + + logAuditEvent({ + action: "provider.credentials.batch_revoked", + actor: "admin", + resourceType: "provider_credentials", + status: "success", + metadata: { count: deleted, ids: body.ids }, + }); + + return NextResponse.json({ message: `Deleted ${deleted} connection(s)`, deleted }); + } catch (error) { + console.log("Error batch deleting connections:", error); + return NextResponse.json({ error: "Failed to batch delete connections" }, { status: 500 }); + } +} +``` + +### 3. UI Layer (`src/app/(dashboard)/dashboard/providers/[id]/page.tsx`) + +#### New State Variables + +```typescript +// Batch selection state +const [selectedIds, setSelectedIds] = useState>(new Set()); +const [batchDeleting, setBatchDeleting] = useState(false); +``` + +#### New Functions + +```typescript +const handleToggleSelectAll = useCallback(() => { + setSelectedIds((prev) => + prev.size === connections.length ? new Set() : new Set(connections.map((c) => c.id)) + ); +}, [connections]); + +const handleToggleSelectOne = useCallback((id: string) => { + setSelectedIds((prev) => { + const next = new Set(prev); + if (next.has(id)) next.delete(id); + else next.add(id); + return next; + }); +}, []); + +const handleBatchDelete = async () => { + if (selectedIds.size === 0) return; + if (!confirm(t("batchDeleteConfirm", { count: selectedIds.size }))) return; + + setBatchDeleting(true); + try { + const res = await fetch("/api/providers", { + method: "DELETE", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ ids: Array.from(selectedIds) }), + }); + + if (res.ok) { + setSelectedIds(new Set()); + await fetchConnections(); + notify.success(t("batchDeleteSuccess", { count: selectedIds.size })); + } else { + const data = await res.json(); + notify.error(data.error || "Batch delete failed"); + } + } catch (error) { + notify.error("Network error during batch delete"); + } finally { + setBatchDeleting(false); + } +}; +``` + +#### Per-Row Checkbox (inside `ConnectionRow`) + +Add a checkbox as the first element of each row: + +```tsx +// In ConnectionRow interface, add: +interface ConnectionRowProps { + isSelected?: boolean; + onToggleSelect?: () => void; + // ... existing props +} + +// In ConnectionRow render, before priority arrows: +
+ + {/* Priority arrows */} + ... +``` + +#### Header Row (above connections list) + +```tsx +
+ + + {selectedIds.size > 0 && ( + + )} +
+``` + +### 4. i18n Keys (to add to all locale files) + +```json +{ + "batchDeleteSelected": "Delete Selected ({count})", + "batchDeleteConfirm": "Delete {count} connection(s)? This action cannot be undone.", + "batchDeleteSuccess": "Deleted {count} connection(s)" +} +``` + +### 5. Testing + +#### Unit Test (`tests/unit/db-providers-crud.test.ts`) + +```typescript +test("deleteProviderConnections deletes multiple connections", async () => { + const ids = [ + (await createProviderConnection({ provider: "openai", name: "test-1", authType: "apikey" })).id!, + (await createProviderConnection({ provider: "openai", name: "test-2", authType: "apikey" })).id!, + ]; + + const deleted = await deleteProviderConnections(ids); + expect(deleted).toBe(2); + + for (const id of ids) { + const conn = await getProviderConnectionById(id); + expect(conn).toBeNull(); + } +}); + +test("deleteProviderConnections with empty array returns 0", async () => { + const deleted = await deleteProviderConnections([]); + expect(deleted).toBe(0); +}); +``` + +#### Integration Test (`tests/integration/api-routes-critical.test.ts`) + +```typescript +test("DELETE /api/providers — batch delete", async () => { + const ids = [conn1.id, conn2.id]; + const res = await fetch("http://localhost:20128/api/providers", { + method: "DELETE", + headers: { "Content-Type": "application/json", "Authorization": `Bearer ${apiKey}` }, + body: JSON.stringify({ ids }), + }); + + expect(res.status).toBe(200); + const data = await res.json(); + expect(data.deleted).toBe(2); +}); +``` + +### UX Details + +1. **Indeterminate select-all**: When some (but not all) rows are selected, the select-all checkbox shows as indeterminate (dash) +2. **Confirmation**: Shows `confirm()` with count ("Delete 3 connections?") +3. **Optimistic update**: Immediately clears selected IDs and removes deleted connections from list on success +4. **Error handling**: Shows error notification; connections remain in list if delete fails +5. **Loading state**: Button shows spinner during delete; row checkboxes disabled +6. **Empty state**: No "Delete Selected" button when nothing selected +7. **Audit logging**: Each batch delete logged as `provider.credentials.batch_revoked` + +### Non-Goals + +- Bulk enable/disable (separate feature) +- Moving selected accounts (separate feature) +- Batch rename/edit (separate feature) +- Deleting across different providers (each provider page operates independently) + +### Risks & Mitigations + +| Risk | Mitigation | +|------|------------| +| User accidentally deletes wrong accounts | Require confirmation dialog with count | +| Too many connections selected | Cap at 100 per batch; show error if exceeded | +| Partial failure on batch delete | DB runs in transaction; all-or-nothing semantics | +| Performance with large selections | Batch SQL with `IN (...)` clause is efficient up to 100 | + +### Coverage + +Per repository rules, this change affects production code in `src/` → automated tests required: +- Unit test for `deleteProviderConnections()` in `tests/unit/db-providers-crud.test.ts` +- Integration test for `DELETE /api/providers` batch endpoint in `tests/integration/api-routes-critical.test.ts` +- Run `npm run test:coverage` — all 4 metrics must meet 60% minimum + +--- + +## Related Issues + +- Closes this issue on merge diff --git a/src/app/(dashboard)/dashboard/providers/[id]/page.tsx b/src/app/(dashboard)/dashboard/providers/[id]/page.tsx index cdd04441bb4..9f505567878 100644 --- a/src/app/(dashboard)/dashboard/providers/[id]/page.tsx +++ b/src/app/(dashboard)/dashboard/providers/[id]/page.tsx @@ -514,6 +514,8 @@ interface ConnectionRowProps { isCodex?: boolean; isFirst: boolean; isLast: boolean; + isSelected?: boolean; + onToggleSelect?: () => void; onMoveUp: () => void; onMoveDown: () => void; onToggleActive: (isActive?: boolean) => void | Promise; @@ -1016,6 +1018,8 @@ export default function ProviderDetailPage() { ); const [applyingCodexAuthId, setApplyingCodexAuthId] = useState(null); const [exportingCodexAuthId, setExportingCodexAuthId] = useState(null); + const [selectedIds, setSelectedIds] = useState>(new Set()); + const [batchDeleting, setBatchDeleting] = useState(false); const isOpenAICompatible = isOpenAICompatibleProvider(providerId); const isCcCompatible = isClaudeCodeCompatibleProvider(providerId); const isAnthropicCompatible = @@ -1348,7 +1352,6 @@ export default function ProviderDetailPage() { const res = await fetch(`/api/providers/${id}`, { method: "DELETE" }); if (res.ok) { setConnections(connections.filter((c) => c.id !== id)); - // Refresh model list after connection deletion (synced models may change) if (providerId === "gemini") { await fetchProviderModelMeta(); } @@ -1358,6 +1361,51 @@ export default function ProviderDetailPage() { } }; + const handleToggleSelectAll = useCallback(() => { + setSelectedIds((prev) => + prev.size === connections.length ? new Set() : new Set(connections.map((c) => c.id)) + ); + }, [connections]); + + const handleToggleSelectOne = useCallback((id: string) => { + setSelectedIds((prev) => { + const next = new Set(prev); + if (next.has(id)) next.delete(id); + else next.add(id); + return next; + }); + }, []); + + const handleBatchDelete = async () => { + if (selectedIds.size === 0) return; + if (!confirm(t("batchDeleteConfirm", { count: selectedIds.size }))) return; + + setBatchDeleting(true); + try { + const res = await fetch("/api/providers", { + method: "DELETE", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ ids: Array.from(selectedIds) }), + }); + + if (res.ok) { + setSelectedIds(new Set()); + await fetchConnections(); + notify.success(t("batchDeleteSuccess", { count: selectedIds.size })); + if (providerId === "gemini") { + await fetchProviderModelMeta(); + } + } else { + const data = await res.json(); + notify.error(data.error || "Batch delete failed"); + } + } catch { + notify.error("Network error during batch delete"); + } finally { + setBatchDeleting(false); + } + }; + const handleOAuthSuccess = useCallback(() => { fetchConnections(); setShowOAuthModal(false); @@ -2912,88 +2960,122 @@ export default function ProviderDetailPage() { ); if (!hasAnyTag) { - // No tags — render flat list as before + const allSelected = selectedIds.size === connections.length && connections.length > 0; + const someSelected = selectedIds.size > 0 && selectedIds.size < connections.length; return ( -
- {sorted.map((conn, index) => ( - handleSwapPriority(conn, sorted[index - 1])} - onMoveDown={() => handleSwapPriority(conn, sorted[index + 1])} - onToggleActive={(isActive) => - handleUpdateConnectionStatus(conn.id, isActive) - } - onToggleRateLimit={(enabled) => handleToggleRateLimit(conn.id, enabled)} - onToggleClaudeExtraUsage={(enabled) => - handleToggleClaudeExtraUsage(conn.id, enabled) - } - isCodex={providerId === "codex"} - isCcCompatible={isCcCompatible} - cliproxyapiEnabled={cpaProviderEnabled} - onToggleCliproxyapiMode={(enabled) => - handleToggleCliproxyapiMode(conn.id, enabled) - } - onToggleCodex5h={(enabled) => - handleToggleCodexLimit(conn.id, "use5h", enabled) - } - onToggleCodexWeekly={(enabled) => - handleToggleCodexLimit(conn.id, "useWeekly", enabled) - } - onRetest={() => handleRetestConnection(conn.id)} - isRetesting={retestingId === conn.id} - onEdit={() => { - setSelectedConnection(conn); - setShowEditModal(true); - }} - onDelete={() => handleDelete(conn.id)} - onReauth={ - conn.authType === "oauth" - ? () => setShowOAuthModal(true, conn) - : undefined - } - onRefreshToken={ - conn.authType === "oauth" ? () => handleRefreshToken(conn.id) : undefined - } - isRefreshing={refreshingId === conn.id} - onApplyCodexAuthLocal={ - providerId === "codex" - ? () => handleApplyCodexAuthLocal(conn.id) - : undefined - } - isApplyingCodexAuthLocal={applyingCodexAuthId === conn.id} - onExportCodexAuthFile={ - providerId === "codex" - ? () => handleExportCodexAuthFile(conn.id) - : undefined - } - isExportingCodexAuthFile={exportingCodexAuthId === conn.id} - onProxy={() => - setProxyTarget({ - level: "key", - id: conn.id, - label: pickDisplayValue( - [conn.name, conn.email], - emailsVisible, - conn.id - ), - }) - } - hasProxy={!!connProxyMap[conn.id]?.proxy} - proxySource={connProxyMap[conn.id]?.level || null} - proxyHost={connProxyMap[conn.id]?.proxy?.host || null} - /> - ))} -
- ); - } + <> +
+ + + {selectedIds.size > 0 && ( + + )} +
+
+ {sorted.map((conn, index) => ( + handleToggleSelectOne(conn.id)} + onMoveUp={() => handleSwapPriority(conn, sorted[index - 1])} + onMoveDown={() => handleSwapPriority(conn, sorted[index + 1])} + onToggleActive={(isActive) => + handleUpdateConnectionStatus(conn.id, isActive) + } + onToggleRateLimit={(enabled) => handleToggleRateLimit(conn.id, enabled)} + onToggleClaudeExtraUsage={(enabled) => + handleToggleClaudeExtraUsage(conn.id, enabled) + } + isCodex={providerId === "codex"} + isCcCompatible={isCcCompatible} + cliproxyapiEnabled={cpaProviderEnabled} + onToggleCliproxyapiMode={(enabled) => + handleToggleCliproxyapiMode(conn.id, enabled) + } + onToggleCodex5h={(enabled) => + handleToggleCodexLimit(conn.id, "use5h", enabled) + } + onToggleCodexWeekly={(enabled) => + handleToggleCodexLimit(conn.id, "useWeekly", enabled) + } + onRetest={() => handleRetestConnection(conn.id)} + isRetesting={retestingId === conn.id} + onEdit={() => { + setSelectedConnection(conn); + setShowEditModal(true); + }} + onDelete={() => handleDelete(conn.id)} + onReauth={ + conn.authType === "oauth" + ? () => setShowOAuthModal(true, conn) + : undefined + } + onRefreshToken={ + conn.authType === "oauth" ? () => handleRefreshToken(conn.id) : undefined + } + isRefreshing={refreshingId === conn.id} + onApplyCodexAuthLocal={ + providerId === "codex" + ? () => handleApplyCodexAuthLocal(conn.id) + : undefined + } + isApplyingCodexAuthLocal={applyingCodexAuthId === conn.id} + onExportCodexAuthFile={ + providerId === "codex" + ? () => handleExportCodexAuthFile(conn.id) + : undefined + } + isExportingCodexAuthFile={exportingCodexAuthId === conn.id} + onProxy={() => + setProxyTarget({ + level: "key", + id: conn.id, + label: pickDisplayValue( + [conn.name, conn.email], + emailsVisible, + conn.id + ), + }) + } + hasProxy={!!connProxyMap[conn.id]?.proxy} + proxySource={connProxyMap[conn.id]?.level || null} + proxyHost={connProxyMap[conn.id]?.proxy?.host || null} + /> + ))} +
+ ); + } - // Build ordered tag groups: untagged first, then alphabetically - const groupMap = new Map(); + // Build ordered tag groups: untagged first, then alphabetically + const groupMap = new Map(); for (const conn of sorted) { const tag = (conn.providerSpecificData?.tag as string | undefined)?.trim() || ""; if (!groupMap.has(tag)) groupMap.set(tag, []); @@ -3043,6 +3125,8 @@ export default function ProviderDetailPage() { isLast={ gi === groupKeys.length - 1 && index === groupConns.length - 1 } + isSelected={selectedIds.has(conn.id)} + onToggleSelect={() => handleToggleSelectOne(conn.id)} onMoveUp={() => handleSwapPriority(conn, sorted[sorted.indexOf(conn) - 1]) } @@ -3059,6 +3143,8 @@ export default function ProviderDetailPage() { handleToggleClaudeExtraUsage(conn.id, enabled) } isCodex={providerId === "codex"} + isCcCompatible={isCcCompatible} + cliproxyapiEnabled={cpaProviderEnabled} onToggleCodex5h={(enabled) => handleToggleCodexLimit(conn.id, "use5h", enabled) } @@ -3117,8 +3203,8 @@ export default function ProviderDetailPage() { })}
); - })() - )} + })()} + )} @@ -4987,6 +5073,8 @@ function ConnectionRow({ cliproxyapiEnabled, isFirst, isLast, + isSelected, + onToggleSelect, onMoveUp, onMoveDown, onToggleActive, @@ -5096,6 +5184,14 @@ function ConnectionRow({ className={`group flex items-center justify-between p-3 rounded-lg hover:bg-black/[0.02] dark:hover:bg-white/[0.02] transition-colors ${connection.isActive === false ? "opacity-60" : ""}`} >
+ {onToggleSelect && ( + + )} {/* Priority arrows */}
) : ( (() => { - // Group connections by tag (providerSpecificData.tag) const sorted = [...connections].sort((a, b) => (a.priority || 0) - (b.priority || 0)); const hasAnyTag = sorted.some( (c) => c.providerSpecificData?.tag as string | undefined ); + const allSelected = selectedIds.size === connections.length && connections.length > 0; + const someSelected = selectedIds.size > 0 && selectedIds.size < connections.length; if (!hasAnyTag) { - const allSelected = selectedIds.size === connections.length && connections.length > 0; - const someSelected = selectedIds.size > 0 && selectedIds.size < connections.length; return ( <>
@@ -3088,7 +3087,40 @@ export default function ProviderDetailPage() { }); return ( -
+ <> + {selectedIds.size > 0 || connections.length > 0 ? ( +
+ + + {selectedIds.size > 0 && ( + + )} +
+ ) : null} +
{groupKeys.map((tag, gi) => { const groupConns = groupMap.get(tag)!; return ( @@ -3199,12 +3231,11 @@ export default function ProviderDetailPage() { ))}
- ); - })} -
- ); - })()} -
+ + ); + } + })()} + )} diff --git a/src/i18n/messages/ar.json b/src/i18n/messages/ar.json index 797cf7ebf2a..a09cd2471df 100644 --- a/src/i18n/messages/ar.json +++ b/src/i18n/messages/ar.json @@ -2336,10 +2336,10 @@ "testKeyLabel": "اختبار مفتاح API", "testKeyPlaceholder": "sk-... (للتحقق فقط)", "providerNotFound": "لم يتم العثور على الموفر", - "deleteConnectionConfirm": "Delete this connection?", - "batchDeleteSelected": "Delete Selected ({count})", - "batchDeleteConfirm": "Delete {count} connection(s)? This action cannot be undone.", - "batchDeleteSuccess": "Deleted {count} connection(s)", + "deleteConnectionConfirm": "هل تريد حذف هذا الاتصال؟", + "batchDeleteSelected": "حذف المحدد ({count})", + "batchDeleteConfirm": "هل تريد حذف {count} اتصال(ات)؟ لا يمكن التراجع عن هذا الإجراء.", + "batchDeleteSuccess": "تم حذف {count} اتصال(ات)", "failedSetAlias": "فشل في تعيين الاسم المستعار", "failedSaveConnection": "فشل حفظ الاتصال", "failedSaveConnectionRetry": "فشل حفظ الاتصال. يرجى المحاولة مرة أخرى.", diff --git a/src/i18n/messages/bg.json b/src/i18n/messages/bg.json index 7e66bbfe88e..03a18a34ecd 100644 --- a/src/i18n/messages/bg.json +++ b/src/i18n/messages/bg.json @@ -2336,7 +2336,7 @@ "testKeyLabel": "Тествайте API ключ", "testKeyPlaceholder": "sk-... (само за проверка)", "providerNotFound": "Доставчикът не е намерен", - "deleteConnectionConfirm": "Delete this connection?", + "deleteConnectionConfirm": "Изтриване на тази връзка?", "batchDeleteSelected": "Delete Selected ({count})", "batchDeleteConfirm": "Delete {count} connection(s)? This action cannot be undone.", "batchDeleteSuccess": "Deleted {count} connection(s)", @@ -4611,4 +4611,4 @@ "noMatchingLogs": "No logs match the current filters.", "tlsFingerprint": "Chrome 124 TLS Fingerprint" } -} +} \ No newline at end of file diff --git a/src/i18n/messages/bn.json b/src/i18n/messages/bn.json index 3b11f652a25..c73de9b2311 100644 --- a/src/i18n/messages/bn.json +++ b/src/i18n/messages/bn.json @@ -4904,4 +4904,4 @@ "noMatchingLogs": "No logs match the current filters.", "tlsFingerprint": "Chrome 124 TLS Fingerprint" } -} +} \ No newline at end of file diff --git a/src/i18n/messages/cs.json b/src/i18n/messages/cs.json index 8c7a66e7864..280ddf2628f 100644 --- a/src/i18n/messages/cs.json +++ b/src/i18n/messages/cs.json @@ -2336,7 +2336,7 @@ "testKeyLabel": "Test API klíč", "testKeyPlaceholder": "sk-... (pouze pro ověření)", "providerNotFound": "Poskytovatel nenalezen", - "deleteConnectionConfirm": "Delete this connection?", + "deleteConnectionConfirm": "Smazat toto připojení?", "batchDeleteSelected": "Delete Selected ({count})", "batchDeleteConfirm": "Delete {count} connection(s)? This action cannot be undone.", "batchDeleteSuccess": "Deleted {count} connection(s)", @@ -4611,4 +4611,4 @@ "noMatchingLogs": "No logs match the current filters.", "tlsFingerprint": "Chrome 124 TLS Fingerprint" } -} +} \ No newline at end of file diff --git a/src/i18n/messages/da.json b/src/i18n/messages/da.json index 345e57529d9..0f84d080a56 100644 --- a/src/i18n/messages/da.json +++ b/src/i18n/messages/da.json @@ -2336,7 +2336,7 @@ "testKeyLabel": "Test API-nøgle", "testKeyPlaceholder": "sk-... (kun til validering)", "providerNotFound": "Udbyder blev ikke fundet", - "deleteConnectionConfirm": "Delete this connection?", + "deleteConnectionConfirm": "Vil du slette denne forbindelse?", "batchDeleteSelected": "Delete Selected ({count})", "batchDeleteConfirm": "Delete {count} connection(s)? This action cannot be undone.", "batchDeleteSuccess": "Deleted {count} connection(s)", @@ -4611,4 +4611,4 @@ "noMatchingLogs": "No logs match the current filters.", "tlsFingerprint": "Chrome 124 TLS Fingerprint" } -} +} \ No newline at end of file diff --git a/src/i18n/messages/de.json b/src/i18n/messages/de.json index 4fefd7908d0..890910940a1 100644 --- a/src/i18n/messages/de.json +++ b/src/i18n/messages/de.json @@ -2336,7 +2336,7 @@ "testKeyLabel": "API-Schlüssel testen", "testKeyPlaceholder": "sk-... (nur zur Validierung)", "providerNotFound": "Anbieter nicht gefunden", - "deleteConnectionConfirm": "Delete this connection?", + "deleteConnectionConfirm": "Diese Verbindung löschen?", "batchDeleteSelected": "Delete Selected ({count})", "batchDeleteConfirm": "Delete {count} connection(s)? This action cannot be undone.", "batchDeleteSuccess": "Deleted {count} connection(s)", @@ -4611,4 +4611,4 @@ "noMatchingLogs": "No logs match the current filters.", "tlsFingerprint": "Chrome 124 TLS Fingerprint" } -} +} \ No newline at end of file diff --git a/src/i18n/messages/es.json b/src/i18n/messages/es.json index 430bee4f746..483f1ceff76 100644 --- a/src/i18n/messages/es.json +++ b/src/i18n/messages/es.json @@ -2336,7 +2336,7 @@ "testKeyLabel": "Clave API de prueba", "testKeyPlaceholder": "sk-... (solo para validación)", "providerNotFound": "Proveedor no encontrado", - "deleteConnectionConfirm": "Delete this connection?", + "deleteConnectionConfirm": "¿Eliminar esta conexión?", "batchDeleteSelected": "Delete Selected ({count})", "batchDeleteConfirm": "Delete {count} connection(s)? This action cannot be undone.", "batchDeleteSuccess": "Deleted {count} connection(s)", @@ -4611,4 +4611,4 @@ "noMatchingLogs": "No logs match the current filters.", "tlsFingerprint": "Chrome 124 TLS Fingerprint" } -} +} \ No newline at end of file diff --git a/src/i18n/messages/fa.json b/src/i18n/messages/fa.json index 8f763046a14..8485958845e 100644 --- a/src/i18n/messages/fa.json +++ b/src/i18n/messages/fa.json @@ -4904,4 +4904,4 @@ "noMatchingLogs": "No logs match the current filters.", "tlsFingerprint": "Chrome 124 TLS Fingerprint" } -} +} \ No newline at end of file diff --git a/src/i18n/messages/fi.json b/src/i18n/messages/fi.json index 33f9a945d72..93fe19790e6 100644 --- a/src/i18n/messages/fi.json +++ b/src/i18n/messages/fi.json @@ -2336,7 +2336,7 @@ "testKeyLabel": "Testaa API-avain", "testKeyPlaceholder": "sk-... (vain vahvistusta varten)", "providerNotFound": "Palveluntarjoajaa ei löydy", - "deleteConnectionConfirm": "Delete this connection?", + "deleteConnectionConfirm": "Poistetaanko tämä yhteys?", "batchDeleteSelected": "Delete Selected ({count})", "batchDeleteConfirm": "Delete {count} connection(s)? This action cannot be undone.", "batchDeleteSuccess": "Deleted {count} connection(s)", @@ -4611,4 +4611,4 @@ "noMatchingLogs": "No logs match the current filters.", "tlsFingerprint": "Chrome 124 TLS Fingerprint" } -} +} \ No newline at end of file diff --git a/src/i18n/messages/fr.json b/src/i18n/messages/fr.json index db56ee44f51..a72d913e747 100644 --- a/src/i18n/messages/fr.json +++ b/src/i18n/messages/fr.json @@ -2336,7 +2336,7 @@ "testKeyLabel": "Clé API de test", "testKeyPlaceholder": "sk-... (pour validation uniquement)", "providerNotFound": "Fournisseur introuvable", - "deleteConnectionConfirm": "Delete this connection?", + "deleteConnectionConfirm": "Supprimer cette connexion ?", "batchDeleteSelected": "Delete Selected ({count})", "batchDeleteConfirm": "Delete {count} connection(s)? This action cannot be undone.", "batchDeleteSuccess": "Deleted {count} connection(s)", @@ -4611,4 +4611,4 @@ "noMatchingLogs": "No logs match the current filters.", "tlsFingerprint": "Chrome 124 TLS Fingerprint" } -} +} \ No newline at end of file diff --git a/src/i18n/messages/gu.json b/src/i18n/messages/gu.json index d0b1986ee17..de27caa6d3e 100644 --- a/src/i18n/messages/gu.json +++ b/src/i18n/messages/gu.json @@ -4904,4 +4904,4 @@ "noMatchingLogs": "No logs match the current filters.", "tlsFingerprint": "Chrome 124 TLS Fingerprint" } -} +} \ No newline at end of file diff --git a/src/i18n/messages/he.json b/src/i18n/messages/he.json index a3c07f65672..eea3ffdf8e6 100644 --- a/src/i18n/messages/he.json +++ b/src/i18n/messages/he.json @@ -2336,7 +2336,7 @@ "testKeyLabel": "בדוק מפתח API", "testKeyPlaceholder": "sk-... (לאימות בלבד)", "providerNotFound": "הספק לא נמצא", - "deleteConnectionConfirm": "Delete this connection?", + "deleteConnectionConfirm": "למחוק את החיבור הזה?", "batchDeleteSelected": "Delete Selected ({count})", "batchDeleteConfirm": "Delete {count} connection(s)? This action cannot be undone.", "batchDeleteSuccess": "Deleted {count} connection(s)", @@ -4611,4 +4611,4 @@ "noMatchingLogs": "No logs match the current filters.", "tlsFingerprint": "Chrome 124 TLS Fingerprint" } -} +} \ No newline at end of file diff --git a/src/i18n/messages/hi.json b/src/i18n/messages/hi.json index f05bc9c4a05..1c11ed28e0f 100644 --- a/src/i18n/messages/hi.json +++ b/src/i18n/messages/hi.json @@ -2336,7 +2336,7 @@ "testKeyLabel": "परीक्षण एपीआई कुंजी", "testKeyPlaceholder": "एसके-... (केवल सत्यापन के लिए)", "providerNotFound": "प्रदाता नहीं मिला", - "deleteConnectionConfirm": "Delete this connection?", + "deleteConnectionConfirm": "यह कनेक्शन हटाएं?", "batchDeleteSelected": "Delete Selected ({count})", "batchDeleteConfirm": "Delete {count} connection(s)? This action cannot be undone.", "batchDeleteSuccess": "Deleted {count} connection(s)", @@ -4611,4 +4611,4 @@ "noMatchingLogs": "No logs match the current filters.", "tlsFingerprint": "Chrome 124 TLS Fingerprint" } -} +} \ No newline at end of file diff --git a/src/i18n/messages/hu.json b/src/i18n/messages/hu.json index 4060418d3a8..895e57efd55 100644 --- a/src/i18n/messages/hu.json +++ b/src/i18n/messages/hu.json @@ -2336,7 +2336,7 @@ "testKeyLabel": "Teszt API-kulcs", "testKeyPlaceholder": "sk-... (csak érvényesítés céljából)", "providerNotFound": "Szolgáltató nem található", - "deleteConnectionConfirm": "Delete this connection?", + "deleteConnectionConfirm": "Törli ezt a kapcsolatot?", "batchDeleteSelected": "Delete Selected ({count})", "batchDeleteConfirm": "Delete {count} connection(s)? This action cannot be undone.", "batchDeleteSuccess": "Deleted {count} connection(s)", @@ -4611,4 +4611,4 @@ "noMatchingLogs": "No logs match the current filters.", "tlsFingerprint": "Chrome 124 TLS Fingerprint" } -} +} \ No newline at end of file diff --git a/src/i18n/messages/id.json b/src/i18n/messages/id.json index 4bae850f4a9..cf21952c897 100644 --- a/src/i18n/messages/id.json +++ b/src/i18n/messages/id.json @@ -2336,7 +2336,7 @@ "testKeyLabel": "Uji Kunci API", "testKeyPlaceholder": "sk-... (untuk validasi saja)", "providerNotFound": "Penyedia tidak ditemukan", - "deleteConnectionConfirm": "Delete this connection?", + "deleteConnectionConfirm": "Hapus koneksi ini?", "batchDeleteSelected": "Delete Selected ({count})", "batchDeleteConfirm": "Delete {count} connection(s)? This action cannot be undone.", "batchDeleteSuccess": "Deleted {count} connection(s)", @@ -4611,4 +4611,4 @@ "noMatchingLogs": "No logs match the current filters.", "tlsFingerprint": "Chrome 124 TLS Fingerprint" } -} +} \ No newline at end of file diff --git a/src/i18n/messages/in.json b/src/i18n/messages/in.json index 24fe69215d8..8df8805eb30 100644 --- a/src/i18n/messages/in.json +++ b/src/i18n/messages/in.json @@ -4904,4 +4904,4 @@ "noMatchingLogs": "No logs match the current filters.", "tlsFingerprint": "Chrome 124 TLS Fingerprint" } -} +} \ No newline at end of file diff --git a/src/i18n/messages/it.json b/src/i18n/messages/it.json index ce0526366fc..37e729b9267 100644 --- a/src/i18n/messages/it.json +++ b/src/i18n/messages/it.json @@ -2336,7 +2336,7 @@ "testKeyLabel": "Testare la chiave API", "testKeyPlaceholder": "sk-... (solo per la convalida)", "providerNotFound": "Fornitore non trovato", - "deleteConnectionConfirm": "Delete this connection?", + "deleteConnectionConfirm": "Eliminare questa connessione?", "batchDeleteSelected": "Delete Selected ({count})", "batchDeleteConfirm": "Delete {count} connection(s)? This action cannot be undone.", "batchDeleteSuccess": "Deleted {count} connection(s)", @@ -4611,4 +4611,4 @@ "noMatchingLogs": "No logs match the current filters.", "tlsFingerprint": "Chrome 124 TLS Fingerprint" } -} +} \ No newline at end of file diff --git a/src/i18n/messages/ja.json b/src/i18n/messages/ja.json index 93f1c00c534..35520cc1d8c 100644 --- a/src/i18n/messages/ja.json +++ b/src/i18n/messages/ja.json @@ -2336,7 +2336,7 @@ "testKeyLabel": "APIキーをテストする", "testKeyPlaceholder": "sk-... (検証のみ)", "providerNotFound": "プロバイダーが見つかりません", - "deleteConnectionConfirm": "Delete this connection?", + "deleteConnectionConfirm": "この接続を削除しますか?", "batchDeleteSelected": "Delete Selected ({count})", "batchDeleteConfirm": "Delete {count} connection(s)? This action cannot be undone.", "batchDeleteSuccess": "Deleted {count} connection(s)", @@ -4611,4 +4611,4 @@ "noMatchingLogs": "No logs match the current filters.", "tlsFingerprint": "Chrome 124 TLS Fingerprint" } -} +} \ No newline at end of file diff --git a/src/i18n/messages/ko.json b/src/i18n/messages/ko.json index 28e490134bc..d992d0fd5ac 100644 --- a/src/i18n/messages/ko.json +++ b/src/i18n/messages/ko.json @@ -2336,7 +2336,7 @@ "testKeyLabel": "테스트 API 키", "testKeyPlaceholder": "sk-...(검증용으로만)", "providerNotFound": "공급자를 찾을 수 없습니다.", - "deleteConnectionConfirm": "Delete this connection?", + "deleteConnectionConfirm": "이 연결을 삭제하시겠습니까?", "batchDeleteSelected": "Delete Selected ({count})", "batchDeleteConfirm": "Delete {count} connection(s)? This action cannot be undone.", "batchDeleteSuccess": "Deleted {count} connection(s)", @@ -4613,4 +4613,4 @@ "noMatchingLogs": "No logs match the current filters.", "tlsFingerprint": "Chrome 124 TLS Fingerprint" } -} +} \ No newline at end of file diff --git a/src/i18n/messages/mr.json b/src/i18n/messages/mr.json index fa4c533ab2e..d47740bea89 100644 --- a/src/i18n/messages/mr.json +++ b/src/i18n/messages/mr.json @@ -4904,4 +4904,4 @@ "noMatchingLogs": "No logs match the current filters.", "tlsFingerprint": "Chrome 124 TLS Fingerprint" } -} +} \ No newline at end of file diff --git a/src/i18n/messages/ms.json b/src/i18n/messages/ms.json index dcd0f9604e6..0ce0eff53a5 100644 --- a/src/i18n/messages/ms.json +++ b/src/i18n/messages/ms.json @@ -2336,7 +2336,7 @@ "testKeyLabel": "Uji Kunci API", "testKeyPlaceholder": "sk-... (untuk pengesahan sahaja)", "providerNotFound": "Pembekal tidak ditemui", - "deleteConnectionConfirm": "Delete this connection?", + "deleteConnectionConfirm": "Padamkan sambungan ini?", "batchDeleteSelected": "Delete Selected ({count})", "batchDeleteConfirm": "Delete {count} connection(s)? This action cannot be undone.", "batchDeleteSuccess": "Deleted {count} connection(s)", @@ -4611,4 +4611,4 @@ "noMatchingLogs": "No logs match the current filters.", "tlsFingerprint": "Chrome 124 TLS Fingerprint" } -} +} \ No newline at end of file diff --git a/src/i18n/messages/nl.json b/src/i18n/messages/nl.json index 7221bd94944..65539efe8d5 100644 --- a/src/i18n/messages/nl.json +++ b/src/i18n/messages/nl.json @@ -2336,7 +2336,7 @@ "testKeyLabel": "API-sleutel testen", "testKeyPlaceholder": "sk-... (alleen ter validatie)", "providerNotFound": "Aanbieder niet gevonden", - "deleteConnectionConfirm": "Delete this connection?", + "deleteConnectionConfirm": "Deze verbinding verwijderen?", "batchDeleteSelected": "Delete Selected ({count})", "batchDeleteConfirm": "Delete {count} connection(s)? This action cannot be undone.", "batchDeleteSuccess": "Deleted {count} connection(s)", @@ -4611,4 +4611,4 @@ "noMatchingLogs": "No logs match the current filters.", "tlsFingerprint": "Chrome 124 TLS Fingerprint" } -} +} \ No newline at end of file diff --git a/src/i18n/messages/no.json b/src/i18n/messages/no.json index cbccf287afe..cf9ddbc9068 100644 --- a/src/i18n/messages/no.json +++ b/src/i18n/messages/no.json @@ -2336,7 +2336,7 @@ "testKeyLabel": "Test API-nøkkel", "testKeyPlaceholder": "sk-... (kun for validering)", "providerNotFound": "Finner ikke leverandør", - "deleteConnectionConfirm": "Delete this connection?", + "deleteConnectionConfirm": "Vil du slette denne tilkoblingen?", "batchDeleteSelected": "Delete Selected ({count})", "batchDeleteConfirm": "Delete {count} connection(s)? This action cannot be undone.", "batchDeleteSuccess": "Deleted {count} connection(s)", @@ -4611,4 +4611,4 @@ "noMatchingLogs": "No logs match the current filters.", "tlsFingerprint": "Chrome 124 TLS Fingerprint" } -} +} \ No newline at end of file diff --git a/src/i18n/messages/phi.json b/src/i18n/messages/phi.json index c37133c33af..0105e325ae8 100644 --- a/src/i18n/messages/phi.json +++ b/src/i18n/messages/phi.json @@ -2336,7 +2336,7 @@ "testKeyLabel": "Subukan ang API Key", "testKeyPlaceholder": "sk-... (para sa pagpapatunay lamang)", "providerNotFound": "Hindi nahanap ang provider", - "deleteConnectionConfirm": "Delete this connection?", + "deleteConnectionConfirm": "Tanggalin ang koneksyon na ito?", "batchDeleteSelected": "Delete Selected ({count})", "batchDeleteConfirm": "Delete {count} connection(s)? This action cannot be undone.", "batchDeleteSuccess": "Deleted {count} connection(s)", @@ -4611,4 +4611,4 @@ "noMatchingLogs": "No logs match the current filters.", "tlsFingerprint": "Chrome 124 TLS Fingerprint" } -} +} \ No newline at end of file diff --git a/src/i18n/messages/pl.json b/src/i18n/messages/pl.json index c7d39873646..4b0028f0fe5 100644 --- a/src/i18n/messages/pl.json +++ b/src/i18n/messages/pl.json @@ -2336,7 +2336,7 @@ "testKeyLabel": "Testuj klucz API", "testKeyPlaceholder": "sk-... (tylko do sprawdzenia)", "providerNotFound": "Nie znaleziono dostawcy", - "deleteConnectionConfirm": "Delete this connection?", + "deleteConnectionConfirm": "Usunąć to połączenie?", "batchDeleteSelected": "Delete Selected ({count})", "batchDeleteConfirm": "Delete {count} connection(s)? This action cannot be undone.", "batchDeleteSuccess": "Deleted {count} connection(s)", @@ -4611,4 +4611,4 @@ "noMatchingLogs": "No logs match the current filters.", "tlsFingerprint": "Chrome 124 TLS Fingerprint" } -} +} \ No newline at end of file diff --git a/src/i18n/messages/pt-BR.json b/src/i18n/messages/pt-BR.json index 44460da6124..542f86ba6e5 100644 --- a/src/i18n/messages/pt-BR.json +++ b/src/i18n/messages/pt-BR.json @@ -2458,7 +2458,7 @@ "testKeyLabel": "Chave de API de Teste", "testKeyPlaceholder": "sk-... (apenas para validação)", "providerNotFound": "Provedor não encontrado", - "deleteConnectionConfirm": "Delete this connection?", + "deleteConnectionConfirm": "Excluir esta conexão?", "batchDeleteSelected": "Delete Selected ({count})", "batchDeleteConfirm": "Delete {count} connection(s)? This action cannot be undone.", "batchDeleteSuccess": "Deleted {count} connection(s)", @@ -4789,4 +4789,4 @@ "noMatchingLogs": "No logs match the current filters.", "tlsFingerprint": "Chrome 124 TLS Fingerprint" } -} +} \ No newline at end of file diff --git a/src/i18n/messages/pt.json b/src/i18n/messages/pt.json index c26140450f6..b260b311863 100644 --- a/src/i18n/messages/pt.json +++ b/src/i18n/messages/pt.json @@ -2408,7 +2408,7 @@ "testKeyLabel": "Chave de API de teste", "testKeyPlaceholder": "sk-... (apenas para validação)", "providerNotFound": "Provedor não encontrado", - "deleteConnectionConfirm": "Delete this connection?", + "deleteConnectionConfirm": "Excluir esta conexão?", "batchDeleteSelected": "Delete Selected ({count})", "batchDeleteConfirm": "Delete {count} connection(s)? This action cannot be undone.", "batchDeleteSuccess": "Deleted {count} connection(s)", @@ -4643,4 +4643,4 @@ "noMatchingLogs": "No logs match the current filters.", "tlsFingerprint": "Chrome 124 TLS Fingerprint" } -} +} \ No newline at end of file diff --git a/src/i18n/messages/ro.json b/src/i18n/messages/ro.json index c88f8d60fd9..f537f20150b 100644 --- a/src/i18n/messages/ro.json +++ b/src/i18n/messages/ro.json @@ -2336,7 +2336,7 @@ "testKeyLabel": "Testați cheia API", "testKeyPlaceholder": "sk-... (doar pentru validare)", "providerNotFound": "Furnizorul nu a fost găsit", - "deleteConnectionConfirm": "Delete this connection?", + "deleteConnectionConfirm": "Ștergeți această conexiune?", "batchDeleteSelected": "Delete Selected ({count})", "batchDeleteConfirm": "Delete {count} connection(s)? This action cannot be undone.", "batchDeleteSuccess": "Deleted {count} connection(s)", @@ -4611,4 +4611,4 @@ "noMatchingLogs": "No logs match the current filters.", "tlsFingerprint": "Chrome 124 TLS Fingerprint" } -} +} \ No newline at end of file diff --git a/src/i18n/messages/ru.json b/src/i18n/messages/ru.json index 4a83292f227..1908caff2fd 100644 --- a/src/i18n/messages/ru.json +++ b/src/i18n/messages/ru.json @@ -2360,7 +2360,7 @@ "testKeyLabel": "Тестовый ключ API", "testKeyPlaceholder": "sk-... (только для проверки)", "providerNotFound": "Провайдер не найден", - "deleteConnectionConfirm": "Delete this connection?", + "deleteConnectionConfirm": "Удалить это соединение?", "batchDeleteSelected": "Delete Selected ({count})", "batchDeleteConfirm": "Delete {count} connection(s)? This action cannot be undone.", "batchDeleteSuccess": "Deleted {count} connection(s)", @@ -4635,4 +4635,4 @@ "noMatchingLogs": "No logs match the current filters.", "tlsFingerprint": "Chrome 124 TLS Fingerprint" } -} +} \ No newline at end of file diff --git a/src/i18n/messages/sk.json b/src/i18n/messages/sk.json index 5f870f13738..cb8e98e97f1 100644 --- a/src/i18n/messages/sk.json +++ b/src/i18n/messages/sk.json @@ -2336,7 +2336,7 @@ "testKeyLabel": "Testovací kľúč API", "testKeyPlaceholder": "sk-... (len na overenie)", "providerNotFound": "Poskytovateľ sa nenašiel", - "deleteConnectionConfirm": "Delete this connection?", + "deleteConnectionConfirm": "Odstrániť toto pripojenie?", "batchDeleteSelected": "Delete Selected ({count})", "batchDeleteConfirm": "Delete {count} connection(s)? This action cannot be undone.", "batchDeleteSuccess": "Deleted {count} connection(s)", @@ -4611,4 +4611,4 @@ "noMatchingLogs": "No logs match the current filters.", "tlsFingerprint": "Chrome 124 TLS Fingerprint" } -} +} \ No newline at end of file diff --git a/src/i18n/messages/sv.json b/src/i18n/messages/sv.json index 702e2300a57..acbe59e7ec6 100644 --- a/src/i18n/messages/sv.json +++ b/src/i18n/messages/sv.json @@ -2336,7 +2336,7 @@ "testKeyLabel": "Testa API-nyckel", "testKeyPlaceholder": "sk-... (endast för validering)", "providerNotFound": "Det gick inte att hitta leverantören", - "deleteConnectionConfirm": "Delete this connection?", + "deleteConnectionConfirm": "Vill du ta bort den här anslutningen?", "batchDeleteSelected": "Delete Selected ({count})", "batchDeleteConfirm": "Delete {count} connection(s)? This action cannot be undone.", "batchDeleteSuccess": "Deleted {count} connection(s)", @@ -4611,4 +4611,4 @@ "noMatchingLogs": "No logs match the current filters.", "tlsFingerprint": "Chrome 124 TLS Fingerprint" } -} +} \ No newline at end of file diff --git a/src/i18n/messages/sw.json b/src/i18n/messages/sw.json index 24fe69215d8..8df8805eb30 100644 --- a/src/i18n/messages/sw.json +++ b/src/i18n/messages/sw.json @@ -4904,4 +4904,4 @@ "noMatchingLogs": "No logs match the current filters.", "tlsFingerprint": "Chrome 124 TLS Fingerprint" } -} +} \ No newline at end of file diff --git a/src/i18n/messages/ta.json b/src/i18n/messages/ta.json index bb336643f1f..b4324669e51 100644 --- a/src/i18n/messages/ta.json +++ b/src/i18n/messages/ta.json @@ -4904,4 +4904,4 @@ "noMatchingLogs": "No logs match the current filters.", "tlsFingerprint": "Chrome 124 TLS Fingerprint" } -} +} \ No newline at end of file diff --git a/src/i18n/messages/te.json b/src/i18n/messages/te.json index 63a191b45bf..145cf4f6e5f 100644 --- a/src/i18n/messages/te.json +++ b/src/i18n/messages/te.json @@ -4904,4 +4904,4 @@ "noMatchingLogs": "No logs match the current filters.", "tlsFingerprint": "Chrome 124 TLS Fingerprint" } -} +} \ No newline at end of file diff --git a/src/i18n/messages/th.json b/src/i18n/messages/th.json index ba8baf732a5..f6152851472 100644 --- a/src/i18n/messages/th.json +++ b/src/i18n/messages/th.json @@ -2336,7 +2336,7 @@ "testKeyLabel": "ทดสอบคีย์ API", "testKeyPlaceholder": "sk-... (สำหรับการตรวจสอบเท่านั้น)", "providerNotFound": "ไม่พบผู้ให้บริการ", - "deleteConnectionConfirm": "Delete this connection?", + "deleteConnectionConfirm": "ลบการเชื่อมต่อนี้ใช่ไหม", "batchDeleteSelected": "Delete Selected ({count})", "batchDeleteConfirm": "Delete {count} connection(s)? This action cannot be undone.", "batchDeleteSuccess": "Deleted {count} connection(s)", @@ -4611,4 +4611,4 @@ "noMatchingLogs": "No logs match the current filters.", "tlsFingerprint": "Chrome 124 TLS Fingerprint" } -} +} \ No newline at end of file diff --git a/src/i18n/messages/tr.json b/src/i18n/messages/tr.json index 285dae67144..8b4d49f5866 100644 --- a/src/i18n/messages/tr.json +++ b/src/i18n/messages/tr.json @@ -2336,7 +2336,7 @@ "testKeyLabel": "API Anahtarını Test Et", "testKeyPlaceholder": "sk-... (yalnızca doğrulama için)", "providerNotFound": "Sağlayıcı bulunamadı", - "deleteConnectionConfirm": "Delete this connection?", + "deleteConnectionConfirm": "Bu bağlantı silinsin mi?", "batchDeleteSelected": "Delete Selected ({count})", "batchDeleteConfirm": "Delete {count} connection(s)? This action cannot be undone.", "batchDeleteSuccess": "Deleted {count} connection(s)", @@ -4611,4 +4611,4 @@ "noMatchingLogs": "No logs match the current filters.", "tlsFingerprint": "Chrome 124 TLS Fingerprint" } -} +} \ No newline at end of file diff --git a/src/i18n/messages/uk-UA.json b/src/i18n/messages/uk-UA.json index 2738e3bc9d1..8aba6ae44e6 100644 --- a/src/i18n/messages/uk-UA.json +++ b/src/i18n/messages/uk-UA.json @@ -2336,7 +2336,7 @@ "testKeyLabel": "Тестовий ключ API", "testKeyPlaceholder": "sk-... (лише для перевірки)", "providerNotFound": "Постачальник не знайдено", - "deleteConnectionConfirm": "Delete this connection?", + "deleteConnectionConfirm": "Видалити це підключення?", "batchDeleteSelected": "Delete Selected ({count})", "batchDeleteConfirm": "Delete {count} connection(s)? This action cannot be undone.", "batchDeleteSuccess": "Deleted {count} connection(s)", @@ -4611,4 +4611,4 @@ "noMatchingLogs": "No logs match the current filters.", "tlsFingerprint": "Chrome 124 TLS Fingerprint" } -} +} \ No newline at end of file diff --git a/src/i18n/messages/ur.json b/src/i18n/messages/ur.json index 347341f3953..991e73849c3 100644 --- a/src/i18n/messages/ur.json +++ b/src/i18n/messages/ur.json @@ -4904,4 +4904,4 @@ "noMatchingLogs": "No logs match the current filters.", "tlsFingerprint": "Chrome 124 TLS Fingerprint" } -} +} \ No newline at end of file diff --git a/src/i18n/messages/vi.json b/src/i18n/messages/vi.json index bbc0979bcf2..ebd621406a5 100644 --- a/src/i18n/messages/vi.json +++ b/src/i18n/messages/vi.json @@ -2336,7 +2336,7 @@ "testKeyLabel": "Khóa API kiểm tra", "testKeyPlaceholder": "sk-... (chỉ để xác nhận)", "providerNotFound": "Không tìm thấy nhà cung cấp", - "deleteConnectionConfirm": "Delete this connection?", + "deleteConnectionConfirm": "Xóa kết nối này?", "batchDeleteSelected": "Delete Selected ({count})", "batchDeleteConfirm": "Delete {count} connection(s)? This action cannot be undone.", "batchDeleteSuccess": "Deleted {count} connection(s)", @@ -4611,4 +4611,4 @@ "noMatchingLogs": "No logs match the current filters.", "tlsFingerprint": "Chrome 124 TLS Fingerprint" } -} +} \ No newline at end of file diff --git a/src/i18n/messages/zh-CN.json b/src/i18n/messages/zh-CN.json index 92f4020bafe..66010b16e48 100644 --- a/src/i18n/messages/zh-CN.json +++ b/src/i18n/messages/zh-CN.json @@ -2435,7 +2435,7 @@ "testKeyLabel": "测试 API 密钥", "testKeyPlaceholder": "sk-...(仅用于验证)", "providerNotFound": "未找到提供商", - "deleteConnectionConfirm": "Delete this connection?", + "deleteConnectionConfirm": "删除这个连接吗?", "batchDeleteSelected": "Delete Selected ({count})", "batchDeleteConfirm": "Delete {count} connection(s)? This action cannot be undone.", "batchDeleteSuccess": "Deleted {count} connection(s)", @@ -4859,4 +4859,4 @@ "noMatchingLogs": "No logs match the current filters.", "tlsFingerprint": "Chrome 124 TLS Fingerprint" } -} +} \ No newline at end of file diff --git a/src/lib/db/providers.ts b/src/lib/db/providers.ts index a551c514ea1..f87953903ae 100644 --- a/src/lib/db/providers.ts +++ b/src/lib/db/providers.ts @@ -437,20 +437,20 @@ export async function deleteProviderConnection(id: string) { export async function deleteProviderConnections(ids: string[]): Promise { if (ids.length === 0) return 0; - const db = getDbInstance() as unknown as DbLike; + const db = getDbInstance(); - const deleteSnapshots = db.prepare("DELETE FROM quota_snapshots WHERE connection_id = ?"); - for (const id of ids) { - deleteSnapshots.run(id); - } - const placeholders = ids.map(() => "?").join(","); - const result = db.prepare( - `DELETE FROM provider_connections WHERE id IN (${placeholders})` - ).run(...ids); + const deletedCount = db.transaction(() => { + const placeholders = ids.map(() => "?").join(","); + db.prepare(`DELETE FROM quota_snapshots WHERE connection_id IN (${placeholders})`).run(...ids); + const result = db.prepare( + `DELETE FROM provider_connections WHERE id IN (${placeholders})` + ).run(...ids); + return result.changes ?? 0; + })(); backupDbFile("pre-write"); invalidateDbCache("connections"); - return result.changes ?? 0; + return deletedCount; } export async function deleteProviderConnectionsByProvider(providerId: string) {