Skip to content

Commit

Permalink
fix: search engine name autogeneration
Browse files Browse the repository at this point in the history
  • Loading branch information
manuel-rw committed Dec 30, 2024
1 parent 799fc67 commit 8a5ba2e
Show file tree
Hide file tree
Showing 3 changed files with 35 additions and 7 deletions.
38 changes: 33 additions & 5 deletions packages/api/src/router/integration/integration-router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { TRPCError } from "@trpc/server";
import { objectEntries } from "@homarr/common";
import { decryptSecret, encryptSecret } from "@homarr/common/server";
import type { Database } from "@homarr/db";
import { and, asc, createId, eq, inArray, like } from "@homarr/db";
import { and, asc, createId, db, eq, inArray, like } from "@homarr/db";
import {
groupMembers,
groupPermissions,
Expand Down Expand Up @@ -202,10 +202,8 @@ export const integrationRouter = createTRPCRouter({
name: input.name,
integrationId,
type: "fromIntegration",
iconUrl:
icon?.url ??
"https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/svg/homarr.svg",
short: input.name.substring(0, 1),
iconUrl: icon?.url ?? "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/svg/homarr.svg",
short: await getNextValidShortNameForSearchEngineAsync(input.name),
});
}
}),
Expand Down Expand Up @@ -427,6 +425,36 @@ interface AddSecretInput {
value: string;
kind: IntegrationSecretKind;
}

const getNextValidShortNameForSearchEngineAsync = async (integrationName: string) => {
const searchEngines = await db.query.searchEngines.findMany({
columns: {
short: true,
},
});

const usedShortNames = searchEngines.flatMap((searchEngine) => searchEngine.short);
const nameByIntegrationName = integrationName.slice(0, 1).toLowerCase();

if (!usedShortNames.includes(nameByIntegrationName)) {
return nameByIntegrationName;
}

// 8 is max length constraint
for (let i = 2; i < 9999999; i++) {
const generatedName = `${nameByIntegrationName}${i}`;
if (usedShortNames.includes(generatedName)) {
continue;
}

return generatedName;
}

throw new Error(
`Unable to automatically generate a short name. All possible variations were exhausted. Please disable the automatic creation and choose one later yourself.`,
);
};

const addSecretAsync = async (db: Database, input: AddSecretInput) => {
await db.insert(integrationSecrets).values({
kind: input.kind,
Expand Down
2 changes: 1 addition & 1 deletion packages/db/schema/mysql.ts
Original file line number Diff line number Diff line change
Expand Up @@ -389,7 +389,7 @@ export const searchEngines = mysqlTable("search_engine", {
id: varchar({ length: 64 }).notNull().primaryKey(),
iconUrl: text().notNull(),
name: varchar({ length: 64 }).notNull(),
short: varchar({ length: 8 }).notNull(),
short: varchar({ length: 8 }).unique().notNull(),
description: text(),
urlTemplate: text(),
type: varchar({ length: 64 }).$type<SearchEngineType>().notNull().default("generic"),
Expand Down
2 changes: 1 addition & 1 deletion packages/db/schema/sqlite.ts
Original file line number Diff line number Diff line change
Expand Up @@ -375,7 +375,7 @@ export const searchEngines = sqliteTable("search_engine", {
id: text().notNull().primaryKey(),
iconUrl: text().notNull(),
name: text().notNull(),
short: text().notNull(),
short: text().unique().notNull(),
description: text(),
urlTemplate: text(),
type: text().$type<SearchEngineType>().notNull().default("generic"),
Expand Down

0 comments on commit 8a5ba2e

Please sign in to comment.