mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-18 13:14:56 +03:00
fix(api): bulk-add API keys no longer overwrite existing connections (#7234)
* fix(api): bulk-add API keys no longer overwrite existing connections createProviderConnection upserts apikey connections BY NAME (same provider + auth_type "apikey" + same name updates the row in place, replacing its apiKey/priority/testStatus instead of inserting). The bulk-add route auto-names unnamed lines "Key 1", "Key 2", ... restarting from 1 on every request, blind to names already saved for the provider — so re-running a bulk paste against a provider that already had "Key 1" silently replaced it instead of adding a new connection alongside it. The same collision could also happen within one batch for two identical custom name|apiKey lines. Add resolveBulkNameCollisions (src/shared/utils/bulkApiKeyParser.ts): gap-fills the smallest free "<name> <n>" suffix against both existing connection names and names already assigned earlier in the same batch, so a name is never reused. Wire it into POST /api/providers/bulk before the create loop, fetching existing apikey connection names via the existing getProviderConnections db module (no raw SQL added to the route). Co-authored-by: asynx6 <sahrulbeni656@gmail.com> Inspired-by: https://github.com/decolua/9router/pull/2587 * chore(changelog): fragment for #7234 --------- Co-authored-by: asynx6 <sahrulbeni656@gmail.com>
This commit is contained in:
committed by
GitHub
parent
046dad5bee
commit
c3fabf34ca
@@ -4,13 +4,19 @@ import {
|
||||
getProviderAuditTarget,
|
||||
summarizeProviderConnectionForAudit,
|
||||
} from "@/lib/compliance/providerAudit";
|
||||
import { createProviderConnection, getProviderNodeById, isCloudEnabled } from "@/models";
|
||||
import {
|
||||
createProviderConnection,
|
||||
getProviderConnections,
|
||||
getProviderNodeById,
|
||||
isCloudEnabled,
|
||||
} from "@/models";
|
||||
import {
|
||||
isAnthropicCompatibleProvider,
|
||||
isOpenAICompatibleProvider,
|
||||
supportsBulkApiKey,
|
||||
} from "@/shared/constants/providers";
|
||||
import { getConsistentMachineId } from "@/shared/utils/machineId";
|
||||
import { resolveBulkNameCollisions } from "@/shared/utils/bulkApiKeyParser";
|
||||
import { syncToCloud } from "@/lib/cloudSync";
|
||||
import { bulkCreateProviderSchema } from "@/shared/validation/schemas";
|
||||
import { isValidationFailure, validateBody } from "@/shared/validation/helpers";
|
||||
@@ -103,11 +109,23 @@ export async function POST(request: Request) {
|
||||
null
|
||||
: null;
|
||||
|
||||
// #2587 — createProviderConnection upserts apikey connections BY NAME, so a
|
||||
// bulk-add name that collides with an already-saved connection (or with
|
||||
// another entry in the same batch) would silently REPLACE that connection's
|
||||
// apiKey/priority/testStatus instead of inserting a new one. Resolve every
|
||||
// collision up front by gap-filling a free "<name> <n>" suffix so each entry
|
||||
// reaches createProviderConnection as a genuine insert.
|
||||
const existingConnections = await getProviderConnections({ provider, authType: "apikey" });
|
||||
const existingNames = existingConnections
|
||||
.map((c) => (typeof c.name === "string" ? c.name : null))
|
||||
.filter((n): n is string => !!n);
|
||||
const resolvedEntries = resolveBulkNameCollisions(entries, existingNames);
|
||||
|
||||
const created: Array<Record<string, unknown>> = [];
|
||||
const errors: Array<{ index: number; name: string; message: string }> = [];
|
||||
|
||||
for (let i = 0; i < entries.length; i++) {
|
||||
const entry = entries[i];
|
||||
for (let i = 0; i < resolvedEntries.length; i++) {
|
||||
const entry = resolvedEntries[i];
|
||||
try {
|
||||
// Per-entry copy so each connection gets its own providerSpecificData. Cloudflare
|
||||
// Workers AI carries a per-key accountId (name|accountId|apiKey) that must NOT bleed
|
||||
|
||||
Reference in New Issue
Block a user