mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-07 15:52:52 +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
@@ -111,3 +111,57 @@ export function parseBulkApiKeys(
|
||||
}
|
||||
|
||||
export const BULK_API_KEY_MAX_LINES = MAX_BULK_LINES;
|
||||
|
||||
// Strips a trailing " <digits>" suffix so a colliding name's numeric index can be
|
||||
// regenerated instead of stacking (e.g. "Key 1" -> "Key", not "Key 1 2").
|
||||
function stripTrailingIndex(name: string): string {
|
||||
const stripped = name.replace(/\s+\d+$/, "");
|
||||
return stripped.length > 0 ? stripped : name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves name collisions across a batch of bulk-add entries.
|
||||
*
|
||||
* Background: `createProviderConnection` upserts apikey connections BY NAME
|
||||
* (see `src/lib/db/providers.ts` — same provider + auth_type "apikey" + same
|
||||
* `name` updates the existing row instead of inserting a new one, replacing
|
||||
* its `apiKey`/`priority`/`testStatus`). `parseBulkApiKeys` auto-names
|
||||
* unnamed lines "Key 1", "Key 2", ... per request, blind to names already
|
||||
* saved for the provider — and a batch can also contain the same custom
|
||||
* `name|apiKey` name twice. Either case previously reached the backend upsert
|
||||
* path and silently overwrote (or self-collapsed) an existing connection
|
||||
* instead of inserting a new one.
|
||||
*
|
||||
* This resolves every collision — against `existingNames` AND against names
|
||||
* already assigned earlier in the same batch — by gap-filling the smallest
|
||||
* free "<base> <n>" suffix, so a name is never reused and every entry reaches
|
||||
* the backend as a genuine insert.
|
||||
*/
|
||||
export function resolveBulkNameCollisions<T extends { name: string }>(
|
||||
entries: T[],
|
||||
existingNames: readonly string[] | null | undefined
|
||||
): T[] {
|
||||
const used = new Set(
|
||||
(Array.isArray(existingNames) ? existingNames : [])
|
||||
.filter((n): n is string => typeof n === "string" && n.length > 0)
|
||||
.map((n) => n.toLowerCase())
|
||||
);
|
||||
|
||||
return entries.map((entry) => {
|
||||
const lowerName = entry.name.toLowerCase();
|
||||
if (!used.has(lowerName)) {
|
||||
used.add(lowerName);
|
||||
return entry;
|
||||
}
|
||||
|
||||
const base = stripTrailingIndex(entry.name);
|
||||
let idx = 1;
|
||||
let candidate = `${base} ${idx}`;
|
||||
while (used.has(candidate.toLowerCase())) {
|
||||
idx += 1;
|
||||
candidate = `${base} ${idx}`;
|
||||
}
|
||||
used.add(candidate.toLowerCase());
|
||||
return { ...entry, name: candidate };
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user