Repository: homarr-labs/homarr
Pull request #2663: feat: add default search engines seeding / set homarr docs as global server search engine
Author: login: ajnart
Created: 2025-03-21T15:52:11Z | Merged: 2025-03-26T20:25:13Z

<br/>
<div align="center">
  <img src="https://homarr.dev/img/logo.png" height="80" alt="" />
  <h3>Homarr</h3>
</div>

Added the following search engines as default during the seeding process. Feel free to directly edit the code if you'd want to use another one than Google / add more search engines. 
I also set Homarr docs as the default search engine 

<img width="624" alt="image" src="https://github.com/user-attachments/assets/5b6feb56-f034-42e7-a08a-3af8bf882058" />




Diff:
diff --git a/packages/db/migrations/seed.ts b/packages/db/migrations/seed.ts
index fa86e77a3c..cb4fac094d 100644
--- a/packages/db/migrations/seed.ts
+++ b/packages/db/migrations/seed.ts
@@ -1,18 +1,22 @@
-import SuperJSON from "superjson";
-
 import { objectKeys } from "@homarr/common";
-import { everyoneGroup } from "@homarr/definitions";
+import { createDocumentationLink, everyoneGroup } from "@homarr/definitions";
 import { defaultServerSettings, defaultServerSettingsKeys } from "@homarr/server-settings";
 
-import { createId, eq } from "..";
 import type { Database } from "..";
-import { onboarding, serverSettings } from "../schema";
+import { createId, eq } from "..";
+import {
+  getServerSettingByKeyAsync,
+  insertServerSettingByKeyAsync,
+  updateServerSettingByKeyAsync,
+} from "../queries/server-setting";
+import { onboarding, searchEngines } from "../schema";
 import { groups } from "../schema/mysql";
 
 export const seedDataAsync = async (db: Database) => {
   await seedEveryoneGroupAsync(db);
   await seedOnboardingAsync(db);
   await seedServerSettingsAsync(db);
+  await seedDefaultSearchEnginesAsync(db);
 };
 
 const seedEveryoneGroupAsync = async (db: Database) => {
@@ -48,21 +52,73 @@ const seedOnboardingAsync = async (db: Database) => {
   console.log("Created onboarding step through seed");
 };
 
+const seedDefaultSearchEnginesAsync = async (db: Database) => {
+  const existingSearchEngines = await db.$count(searchEngines);
+
+  if (existingSearchEngines > 0) {
+    console.log("Skipping seeding of default search engines as some already exists");
+    return;
+  }
+
+  const homarrId = createId();
+  const defaultSearchEngines = [
+    {
+      id: createId(),
+      name: "Google",
+      iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/google.svg",
+      short: "g",
+      description: "Search the web with Google",
+      urlTemplate: "https://www.google.com/search?q=%s",
+      type: "generic" as const,
+      integrationId: null,
+    },
+    {
+      id: createId(),
+      name: "YouTube",
+      iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/youtube.svg",
+      short: "yt",
+      description: "Search for videos on YouTube",
+      urlTemplate: "https://www.youtube.com/results?search_query=%s",
+      type: "generic" as const,
+      integrationId: null,
+    },
+    {
+      id: homarrId,
+      name: "Homarr Docs",
+      iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/homarr.svg",
+      short: "docs",
+      description: "Search the Homarr documentation",
+      urlTemplate: createDocumentationLink("/search", undefined, { q: "%s" }),
+      type: "generic" as const,
+      integrationId: null,
+    },
+  ];
+
+  await db.insert(searchEngines).values(defaultSearchEngines);
+  console.log(`Created ${defaultSearchEngines.length} default search engines through seeding process`);
+
+  // Set Homarr docs as the default search engine in server settings
+  const searchSettings = await getServerSettingByKeyAsync(db, "search");
+
+  await updateServerSettingByKeyAsync(db, "search", {
+    ...searchSettings,
+    defaultSearchEngineId: homarrId,
+  });
+  console.log("Set Homarr docs as the default search engine");
+};
+
 const seedServerSettingsAsync = async (db: Database) => {
   const serverSettingsData = await db.query.serverSettings.findMany();
 
   for (const settingsKey of defaultServerSettingsKeys) {
     const currentDbEntry = serverSettingsData.find((setting) => setting.settingKey === settingsKey);
     if (!currentDbEntry) {
-      await db.insert(serverSettings).values({
-        settingKey: settingsKey,
-        value: SuperJSON.stringify(defaultServerSettings[settingsKey]),
-      });
+      await insertServerSettingByKeyAsync(db, settingsKey, defaultServerSettings[settingsKey]);
       console.log(`Created serverSetting through seed key=${settingsKey}`);
       continue;
     }
 
-    const currentSettings = SuperJSON.parse<Record<string, unknown>>(currentDbEntry.value);
+    const currentSettings = await getServerSettingByKeyAsync(db, settingsKey);
     const defaultSettings = defaultServerSettings[settingsKey];
     const missingKeys = objectKeys(defaultSettings).filter((key) => !(key in currentSettings));
 
@@ -71,12 +127,7 @@ const seedServerSettingsAsync = async (db: Database) => {
       continue;
     }
 
-    await db
-      .update(serverSettings)
-      .set({
-        value: SuperJSON.stringify({ ...defaultSettings, ...currentSettings }), // Add missing keys
-      })
-      .where(eq(serverSettings.settingKey, settingsKey));
+    await updateServerSettingByKeyAsync(db, settingsKey, { ...defaultSettings, ...currentSettings });
     console.log(`Updated serverSetting through seed key=${settingsKey}`);
   }
 };
diff --git a/packages/db/queries/server-setting.ts b/packages/db/queries/server-setting.ts
index 73f5156bcb..371b18f292 100644
--- a/packages/db/queries/server-setting.ts
+++ b/packages/db/queries/server-setting.ts
@@ -50,3 +50,14 @@ export const updateServerSettingByKeyAsync = async <TKey extends keyof ServerSet
     })
     .where(eq(serverSettings.settingKey, key));
 };
+
+export const insertServerSettingByKeyAsync = async <TKey extends keyof ServerSettings>(
+  db: Database,
+  key: TKey,
+  value: ServerSettings[TKey],
+) => {
+  await db.insert(serverSettings).values({
+    settingKey: key,
+    value: SuperJSON.stringify(value),
+  });
+};
diff --git a/packages/definitions/src/docs/index.ts b/packages/definitions/src/docs/index.ts
index 5d3c11f5cf..af67312393 100644
--- a/packages/definitions/src/docs/index.ts
+++ b/packages/definitions/src/docs/index.ts
@@ -3,5 +3,12 @@ import type { HomarrDocumentationPath } from "./homarr-docs-sitemap";
 const documentationBaseUrl = "https://homarr.dev";
 
 // Please use the method so the path can be checked!
-export const createDocumentationLink = (path: HomarrDocumentationPath, hashTag?: `#${string}`) =>
-  `${documentationBaseUrl}${path}${hashTag ?? ""}`;
+export const createDocumentationLink = (
+  path: HomarrDocumentationPath,
+  hashTag?: `#${string}`,
+  queryParams?: Record<string, string>,
+) => {
+  const url = `${documentationBaseUrl}${path}`;
+  const params = queryParams ? `?${new URLSearchParams(queryParams)}` : "";
+  return `${url}${params}${hashTag ?? ""}`;
+};
diff --git a/packages/definitions/src/test/docs.spec.ts b/packages/definitions/src/test/docs.spec.ts
new file mode 100644
index 0000000000..d7690f22eb
--- /dev/null
+++ b/packages/definitions/src/test/docs.spec.ts
@@ -0,0 +1,47 @@
+/* eslint-disable no-restricted-syntax */
+import { describe, expect, test } from "vitest";
+
+import { createDocumentationLink } from "../docs";
+import type { HomarrDocumentationPath } from "../docs/homarr-docs-sitemap";
+
+describe("createDocumentationLink should generate correct URLs", () => {
+  test.each([
+    ["/docs/getting-started", undefined, undefined, "https://homarr.dev/docs/getting-started"],
+    ["/blog", undefined, undefined, "https://homarr.dev/blog"],
+    ["/docs/widgets/weather", "#configuration", undefined, "https://homarr.dev/docs/widgets/weather#configuration"],
+    [
+      "/docs/advanced/environment-variables",
+      undefined,
+      { lang: "en" },
+      "https://homarr.dev/docs/advanced/environment-variables?lang=en",
+    ],
+    [
+      "/docs/widgets/bookmarks",
+      "#sorting",
+      { lang: "fr", theme: "dark" },
+      "https://homarr.dev/docs/widgets/bookmarks?lang=fr&theme=dark#sorting",
+    ],
+  ] satisfies [HomarrDocumentationPath, `#${string}` | undefined, Record<string, string> | undefined, string][])(
+    "should create correct URL for path %s with hash %s and params %o",
+    (path, hashTag, queryParams, expected) => {
+      expect(createDocumentationLink(path, hashTag, queryParams)).toBe(expected);
+    },
+  );
+});
+
+describe("createDocumentationLink parameter validation", () => {
+  test("should work with only path parameter", () => {
+    const result = createDocumentationLink("/docs/getting-started");
+    expect(result).toBe("https://homarr.dev/docs/getting-started");
+  });
+
+  test("should work with path and hashtag", () => {
+    const result = createDocumentationLink("/docs/getting-started", "#installation");
+    expect(result).toBe("https://homarr.dev/docs/getting-started#installation");
+  });
+
+  test("should work with path and query params", () => {
+    const result = createDocumentationLink("/docs/getting-started", undefined, { version: "1.0" });
+    expect(result).toBe("https://homarr.dev/docs/getting-started?version=1.0");
+  });
+});


Reviews:
user: login: Meierschlumpf
state: CHANGES_REQUESTED
author_association: MEMBER
submitted_at: 2025-03-21T16:07:25Z
user: login: Meierschlumpf
state: CHANGES_REQUESTED
author_association: MEMBER
submitted_at: 2025-03-21T16:23:31Z
user: login: Meierschlumpf
body: In general fix the failing CI and my comment. Then it is good to go
state: DISMISSED
author_association: MEMBER
submitted_at: 2025-03-22T08:56:48Z
user: login: Meierschlumpf
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-23T17:49:08Z
user: login: ajnart
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-23T18:14:21Z
user: login: Meierschlumpf
state: DISMISSED
author_association: MEMBER
submitted_at: 2025-03-25T20:02:48Z
user: login: ajnart
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-26T14:36:54Z
user: login: Meierschlumpf
body: Thanks for the tests ❤️ 
state: APPROVED
author_association: MEMBER
submitted_at: 2025-03-26T20:24:41Z

Review comments:
diff_hunk: @@ -48,6 +49,77 @@ const seedOnboardingAsync = async (db: Database) => {
   console.log("Created onboarding step through seed");
 };
 
+const seedDefaultSearchEnginesAsync = async (db: Database) => {
+  const existingSearchEngines = await db.query.searchEngines.findMany();
+  
+  if (existingSearchEngines.length > 0) {
+    console.log("Skipping seeding of default search engines as some already exists");
+    return;
+  }
+
+  const homarrId = createId();
+  const defaultSearchEngines = [
+    {
+      id: createId(),
+      name: "Google",
+      iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/google.svg",
+      short: "g",
+      description: "Search the web with Google",
+      urlTemplate: "https://www.google.com/search?q=%s",
+      type: "generic" as const,
+      integrationId: null,
+    },
+    {
+      id: createId(),
+      name: "YouTube",
+      iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/youtube.svg",
+      short: "yt",
+      description: "Search for videos on YouTube",
+      urlTemplate: "https://www.youtube.com/results?search_query=%s",
+      type: "generic" as const,
+      integrationId: null,
+    },
+    {
+      id: homarrId,
+      name: "Homarr Docs",
+      iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/homarr.svg",
+      short: "docs",
+      description: "Search the Homarr documentation",
+      urlTemplate: "https://homarr.dev/search?q=%s",
+      type: "generic" as const,
+      integrationId: null,
+    },
+  ];
+
+  await db.insert(searchEngines).values(defaultSearchEngines);
+  console.log(`Created ${defaultSearchEngines.length} default search engines through seeding process`);
+  
+  // Set Homarr docs as the default search engine in server settings
+  const searchSettings = await db.query.serverSettings.findFirst({
path: packages/db/migrations/seed.ts
user: login: Meierschlumpf
body: There are methods under queries in the db package that already handle superjson parsing and have full typesafety 
created_at: 2025-03-21T16:07:19Z
author_association: MEMBER
diff_hunk: @@ -48,21 +48,82 @@ const seedOnboardingAsync = async (db: Database) => {
   console.log("Created onboarding step through seed");
 };
 
+const seedDefaultSearchEnginesAsync = async (db: Database) => {
+  const existingSearchEngines = await db.query.searchEngines.findMany();
path: packages/db/migrations/seed.ts
user: login: Meierschlumpf
body: Use `db.$count(searchEngines)` instead 
created_at: 2025-03-21T16:20:01Z
author_association: MEMBER
diff_hunk: @@ -48,21 +48,82 @@ const seedOnboardingAsync = async (db: Database) => {
   console.log("Created onboarding step through seed");
 };
 
+const seedDefaultSearchEnginesAsync = async (db: Database) => {
+  const existingSearchEngines = await db.query.searchEngines.findMany();
+  
+  if (existingSearchEngines.length > 0) {
+    console.log("Skipping seeding of default search engines as some already exists");
+    return;
+  }
+
+  const homarrId = createId();
+  const defaultSearchEngines = [
+    {
+      id: createId(),
+      name: "Google",
+      iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/google.svg",
+      short: "g",
+      description: "Search the web with Google",
+      urlTemplate: "https://www.google.com/search?q=%s",
+      type: "generic" as const,
+      integrationId: null,
+    },
+    {
+      id: createId(),
+      name: "YouTube",
+      iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/youtube.svg",
+      short: "yt",
+      description: "Search for videos on YouTube",
+      urlTemplate: "https://www.youtube.com/results?search_query=%s",
+      type: "generic" as const,
+      integrationId: null,
+    },
+    {
+      id: homarrId,
+      name: "Homarr Docs",
+      iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/homarr.svg",
+      short: "docs",
+      description: "Search the Homarr documentation",
+      urlTemplate: "https://homarr.dev/search?q=%s",
+      type: "generic" as const,
+      integrationId: null,
+    },
+  ];
+
+  await db.insert(searchEngines).values(defaultSearchEngines);
+  console.log(`Created ${defaultSearchEngines.length} default search engines through seeding process`);
+  
+  // Set Homarr docs as the default search engine in server settings
+  const searchSettings = await getServerSettingByKeyAsync(db, "search");
+  
+  if (searchSettings) {
+    if (!searchSettings.defaultSearchEngineId) {
+      await updateServerSettingByKeyAsync(db, "search", {
+        ...searchSettings,
+        defaultSearchEngineId: homarrId
+      });
+      console.log("Set Homarr docs as the default search engine");
+    }
+  } else {
+    await updateServerSettingByKeyAsync(db, "search", { 
+      defaultSearchEngineId: homarrId 
+    });
+    console.log("Created search settings with Homarr docs as the default search engine");
+  }
+};
+
 const seedServerSettingsAsync = async (db: Database) => {
   const serverSettingsData = await db.query.serverSettings.findMany();
 
   for (const settingsKey of defaultServerSettingsKeys) {
     const currentDbEntry = serverSettingsData.find((setting) => setting.settingKey === settingsKey);
     if (!currentDbEntry) {
-      await db.insert(serverSettings).values({
-        settingKey: settingsKey,
-        value: SuperJSON.stringify(defaultServerSettings[settingsKey]),
-      });
+      await updateServerSettingByKeyAsync(db, settingsKey, defaultServerSettings[settingsKey]);
path: packages/db/migrations/seed.ts
user: login: Meierschlumpf
body: This does not work as it has to be inserted instead of updated
created_at: 2025-03-21T16:23:01Z
author_association: MEMBER
diff_hunk: @@ -48,21 +52,82 @@ const seedOnboardingAsync = async (db: Database) => {
   console.log("Created onboarding step through seed");
 };
 
+const seedDefaultSearchEnginesAsync = async (db: Database) => {
+  const existingSearchEngines = await db.$count(searchEngines);
+
+  if (existingSearchEngines > 0) {
+    console.log("Skipping seeding of default search engines as some already exists");
+    return;
+  }
+
+  const homarrId = createId();
+  const defaultSearchEngines = [
+    {
+      id: createId(),
+      name: "Google",
+      iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/google.svg",
+      short: "g",
+      description: "Search the web with Google",
+      urlTemplate: "https://www.google.com/search?q=%s",
+      type: "generic" as const,
+      integrationId: null,
+    },
+    {
+      id: createId(),
+      name: "YouTube",
+      iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/youtube.svg",
+      short: "yt",
+      description: "Search for videos on YouTube",
+      urlTemplate: "https://www.youtube.com/results?search_query=%s",
+      type: "generic" as const,
+      integrationId: null,
+    },
+    {
+      id: homarrId,
+      name: "Homarr Docs",
+      iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/homarr.svg",
+      short: "docs",
+      description: "Search the Homarr documentation",
+      urlTemplate: "https://homarr.dev/search?q=%s",
+      type: "generic" as const,
+      integrationId: null,
+    },
+  ];
+
+  await db.insert(searchEngines).values(defaultSearchEngines);
+  console.log(`Created ${defaultSearchEngines.length} default search engines through seeding process`);
+
+  // Set Homarr docs as the default search engine in server settings
+  const searchSettings = await getServerSettingByKeyAsync(db, "search");
+
+  if (searchSettings) {
+    if (!searchSettings.defaultSearchEngineId) {
+      await updateServerSettingByKeyAsync(db, "search", {
+        ...searchSettings,
+        defaultSearchEngineId: homarrId,
+      });
+      console.log("Set Homarr docs as the default search engine");
+    }
+  } else {
+    await updateServerSettingByKeyAsync(db, "search", {
+      defaultSearchEngineId: homarrId,
+    });
+    console.log("Created search settings with Homarr docs as the default search engine");
+  }
path: packages/db/migrations/seed.ts
user: login: Meierschlumpf
body: Remove this as the server-settings are always initialized before it and so this is not necessary (and would be wrong if we would like to create it) 
created_at: 2025-03-22T08:56:20Z
author_association: MEMBER
diff_hunk: @@ -48,21 +52,82 @@ const seedOnboardingAsync = async (db: Database) => {
   console.log("Created onboarding step through seed");
 };
 
+const seedDefaultSearchEnginesAsync = async (db: Database) => {
+  const existingSearchEngines = await db.$count(searchEngines);
+
+  if (existingSearchEngines > 0) {
+    console.log("Skipping seeding of default search engines as some already exists");
+    return;
+  }
+
+  const homarrId = createId();
+  const defaultSearchEngines = [
+    {
+      id: createId(),
+      name: "Google",
+      iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/google.svg",
+      short: "g",
+      description: "Search the web with Google",
+      urlTemplate: "https://www.google.com/search?q=%s",
+      type: "generic" as const,
+      integrationId: null,
+    },
+    {
+      id: createId(),
+      name: "YouTube",
+      iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/youtube.svg",
+      short: "yt",
+      description: "Search for videos on YouTube",
+      urlTemplate: "https://www.youtube.com/results?search_query=%s",
+      type: "generic" as const,
+      integrationId: null,
+    },
+    {
+      id: homarrId,
+      name: "Homarr Docs",
+      iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/homarr.svg",
+      short: "docs",
+      description: "Search the Homarr documentation",
+      urlTemplate: "https://homarr.dev/search?q=%s",
+      type: "generic" as const,
+      integrationId: null,
+    },
+  ];
+
+  await db.insert(searchEngines).values(defaultSearchEngines);
+  console.log(`Created ${defaultSearchEngines.length} default search engines through seeding process`);
+
+  // Set Homarr docs as the default search engine in server settings
+  const searchSettings = await getServerSettingByKeyAsync(db, "search");
+
+  if (searchSettings) {
+    if (!searchSettings.defaultSearchEngineId) {
+      await updateServerSettingByKeyAsync(db, "search", {
+        ...searchSettings,
+        defaultSearchEngineId: homarrId,
+      });
+      console.log("Set Homarr docs as the default search engine");
+    }
+  } else {
+    await updateServerSettingByKeyAsync(db, "search", {
+      defaultSearchEngineId: homarrId,
+    });
+    console.log("Created search settings with Homarr docs as the default search engine");
+  }
path: packages/db/migrations/seed.ts
user: login: Meierschlumpf
body: What I mean is, that the seeding of the server-settings happens first already, so we will never get into this else clause.
AND
if we would have needed this else clause, then the server settings should not be updated and rather inserted
created_at: 2025-03-23T17:49:08Z
author_association: MEMBER
diff_hunk: @@ -48,21 +52,82 @@ const seedOnboardingAsync = async (db: Database) => {
   console.log("Created onboarding step through seed");
 };
 
+const seedDefaultSearchEnginesAsync = async (db: Database) => {
+  const existingSearchEngines = await db.$count(searchEngines);
+
+  if (existingSearchEngines > 0) {
+    console.log("Skipping seeding of default search engines as some already exists");
+    return;
+  }
+
+  const homarrId = createId();
+  const defaultSearchEngines = [
+    {
+      id: createId(),
+      name: "Google",
+      iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/google.svg",
+      short: "g",
+      description: "Search the web with Google",
+      urlTemplate: "https://www.google.com/search?q=%s",
+      type: "generic" as const,
+      integrationId: null,
+    },
+    {
+      id: createId(),
+      name: "YouTube",
+      iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/youtube.svg",
+      short: "yt",
+      description: "Search for videos on YouTube",
+      urlTemplate: "https://www.youtube.com/results?search_query=%s",
+      type: "generic" as const,
+      integrationId: null,
+    },
+    {
+      id: homarrId,
+      name: "Homarr Docs",
+      iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/homarr.svg",
+      short: "docs",
+      description: "Search the Homarr documentation",
+      urlTemplate: "https://homarr.dev/search?q=%s",
+      type: "generic" as const,
+      integrationId: null,
+    },
+  ];
+
+  await db.insert(searchEngines).values(defaultSearchEngines);
+  console.log(`Created ${defaultSearchEngines.length} default search engines through seeding process`);
+
+  // Set Homarr docs as the default search engine in server settings
+  const searchSettings = await getServerSettingByKeyAsync(db, "search");
+
+  if (searchSettings) {
+    if (!searchSettings.defaultSearchEngineId) {
+      await updateServerSettingByKeyAsync(db, "search", {
+        ...searchSettings,
+        defaultSearchEngineId: homarrId,
+      });
+      console.log("Set Homarr docs as the default search engine");
+    }
+  } else {
+    await updateServerSettingByKeyAsync(db, "search", {
+      defaultSearchEngineId: homarrId,
+    });
+    console.log("Created search settings with Homarr docs as the default search engine");
+  }
path: packages/db/migrations/seed.ts
user: login: ajnart
body: Thanks ! Much clearer now :D I've committed d289fe33c2cba3cabe092b10d8f07ee9e09be461 to address this 
created_at: 2025-03-23T18:14:21Z
author_association: MEMBER
diff_hunk: @@ -48,21 +52,74 @@ const seedOnboardingAsync = async (db: Database) => {
   console.log("Created onboarding step through seed");
 };
 
+const seedDefaultSearchEnginesAsync = async (db: Database) => {
+  const existingSearchEngines = await db.$count(searchEngines);
+
+  if (existingSearchEngines > 0) {
+    console.log("Skipping seeding of default search engines as some already exists");
+    return;
+  }
+
+  const homarrId = createId();
+  const defaultSearchEngines = [
+    {
+      id: createId(),
+      name: "Google",
+      iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/google.svg",
+      short: "g",
+      description: "Search the web with Google",
+      urlTemplate: "https://www.google.com/search?q=%s",
+      type: "generic" as const,
+      integrationId: null,
+    },
+    {
+      id: createId(),
+      name: "YouTube",
+      iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/youtube.svg",
+      short: "yt",
+      description: "Search for videos on YouTube",
+      urlTemplate: "https://www.youtube.com/results?search_query=%s",
+      type: "generic" as const,
+      integrationId: null,
+    },
+    {
+      id: homarrId,
+      name: "Homarr Docs",
+      iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/homarr.svg",
+      short: "docs",
+      description: "Search the Homarr documentation",
+      // eslint-disable-next-line no-restricted-syntax
+      urlTemplate: "https://homarr.dev/search?q=%s",
path: packages/db/migrations/seed.ts
user: login: Meierschlumpf
body: What about using the recommended method and updating it to accept query params 😉 
created_at: 2025-03-25T20:01:26Z
author_association: MEMBER
diff_hunk: @@ -48,21 +52,74 @@ const seedOnboardingAsync = async (db: Database) => {
   console.log("Created onboarding step through seed");
 };
 
+const seedDefaultSearchEnginesAsync = async (db: Database) => {
+  const existingSearchEngines = await db.$count(searchEngines);
+
+  if (existingSearchEngines > 0) {
+    console.log("Skipping seeding of default search engines as some already exists");
+    return;
+  }
+
+  const homarrId = createId();
+  const defaultSearchEngines = [
+    {
+      id: createId(),
+      name: "Google",
+      iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/google.svg",
+      short: "g",
+      description: "Search the web with Google",
+      urlTemplate: "https://www.google.com/search?q=%s",
+      type: "generic" as const,
+      integrationId: null,
+    },
+    {
+      id: createId(),
+      name: "YouTube",
+      iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/youtube.svg",
+      short: "yt",
+      description: "Search for videos on YouTube",
+      urlTemplate: "https://www.youtube.com/results?search_query=%s",
+      type: "generic" as const,
+      integrationId: null,
+    },
+    {
+      id: homarrId,
+      name: "Homarr Docs",
+      iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/homarr.svg",
+      short: "docs",
+      description: "Search the Homarr documentation",
+      // eslint-disable-next-line no-restricted-syntax
+      urlTemplate: "https://homarr.dev/search?q=%s",
path: packages/db/migrations/seed.ts
user: login: ajnart
body: You're right! I did that and added tests as well 👍🏼 
created_at: 2025-03-26T14:36:54Z
author_association: MEMBER

PR comments:
user: login: deepsource-io[bot]
created_at: 2025-03-21T15:54:00Z
body: <div><p>Here's the code health analysis summary for commits <code>3edfe6b..782ea36</code>. <a href="https://app.deepsource.com/gh/homarr-labs/homarr/run/9df92c51-2b81-47e8-8b02-507564c44013/">View details on DeepSource</a>&nbsp;↗.</p><h3>Analysis Summary</h3><table><thead><tr><th>Analyzer</th><th>Status</th><th>Summary</th><th>Link</th></tr></thead><tbody><tr><td><img src="https://static.deepsource.com/analyzer_logos/javascript.svg" alt="DeepSource JavaScript Logo" width="16px" height="16px"/><strong>JavaScript</strong></td><td><span>✅&nbsp;</span><span>Success</span></td><td></td><td><a href="https://app.deepsource.com/gh/homarr-labs/homarr/run/9df92c51-2b81-47e8-8b02-507564c44013/javascript/">View Check</a>&nbsp;↗</td></tr></tbody></table><hr/><blockquote><div>💡 If you’re a repository administrator, you can configure the quality gates from the <a href="https://app.deepsource.com/gh/homarr-labs/homarr/settings/reporting">settings</a>.</div></blockquote></div>

author_association: CONTRIBUTOR
user: login: github-actions[bot]
created_at: 2025-03-21T15:55:10Z
body: <h2>Coverage Report</h2>
<table> <thead> <tr> <th align="center">Status</th> <th align="left">Category</th> <th align="right">Percentage</th> <th align="right">Covered / Total</th> </tr> </thead> <tbody> <tr> <td align="center">🔵</td> <td align="left">Lines</td> <td align="right">22.73%</td> <td align="right">10262 / 45131</td> </tr> <tr> <td align="center">🔵</td> <td align="left">Statements</td> <td align="right">22.73%</td> <td align="right">10262 / 45131</td> </tr> <tr> <td align="center">🔵</td> <td align="left">Functions</td> <td align="right">29.88%</td> <td align="right">427 / 1429</td> </tr> <tr> <td align="center">🔵</td> <td align="left">Branches</td> <td align="right">64.76%</td> <td align="right">1235 / 1907</td> </tr> </tbody> </table><details><summary>File Coverage</summary><table> <thead> <tr> <th align="left">File</th> <th align="right">Stmts</th> <th align="right">Branches</th> <th align="right">Functions</th> <th align="right">Lines</th> <th align="left">Uncovered Lines</th> </tr> </thead> <tbody> <tr> <td align="left" colspan="6"><b>Changed Files</b></td> </tr> <tr> <td align="left"><a href="https://github.com/homarr-labs/homarr/blob/782ea36b737bbe883f5ea9184917522148336109/packages/db/migrations/seed.ts">packages/db/migrations/seed.ts</a></td> <td align="right">0%</td> <td align="right">0%</td> <td align="right">0%</td> <td align="right">0%</td> <td align="left"><a href="https://github.com/homarr-labs/homarr/blob/782ea36b737bbe883f5ea9184917522148336109/packages/db/migrations/seed.ts#L1-L133" class="text-red">1-133</a></td> </tr> <tr> <td align="left"><a href="https://github.com/homarr-labs/homarr/blob/782ea36b737bbe883f5ea9184917522148336109/packages/db/queries/server-setting.ts">packages/db/queries/server-setting.ts</a></td> <td align="right">70.21%</td> <td align="right">85.71%</td> <td align="right">75%</td> <td align="right">70.21%</td> <td align="left"><a href="https://github.com/homarr-labs/homarr/blob/782ea36b737bbe883f5ea9184917522148336109/packages/db/queries/server-setting.ts#L21-L25" class="text-red">21-25</a>, <a href="https://github.com/homarr-labs/homarr/blob/782ea36b737bbe883f5ea9184917522148336109/packages/db/queries/server-setting.ts#L55-L63" class="text-red">55-63</a></td> </tr> <tr> <td align="left"><a href="https://github.com/homarr-labs/homarr/blob/782ea36b737bbe883f5ea9184917522148336109/packages/definitions/src/docs/index.ts">packages/definitions/src/docs/index.ts</a></td> <td align="right">100%</td> <td align="right">100%</td> <td align="right">100%</td> <td align="right">100%</td> <td align="left"></td> </tr> </tbody> </table></details>
<em>Generated in workflow <a href=https://github.com/homarr-labs/homarr/actions/runs/14087588120>#5659</a> for commit <a href="https://github.com/homarr-labs/homarr/commit/782ea36b737bbe883f5ea9184917522148336109">782ea36</a> by the <a href="https://github.com/davelosert/vitest-coverage-report-action">Vitest Coverage Report Action</a></em>

<!-- vitest-coverage-report-marker-root -->
author_association: CONTRIBUTOR