From 26b6ca24457e3a80226714b02776c1902b0cb865 Mon Sep 17 00:00:00 2001 From: SeDemal Date: Wed, 18 Dec 2024 14:14:31 +0100 Subject: [PATCH] refactor: Move integration search flag to categories (#1637) --- .../router/integration/integration-router.ts | 33 ++++----------- packages/definitions/src/integration.ts | 42 ++----------------- 2 files changed, 10 insertions(+), 65 deletions(-) diff --git a/packages/api/src/router/integration/integration-router.ts b/packages/api/src/router/integration/integration-router.ts index 54963407e..72378a7e9 100644 --- a/packages/api/src/router/integration/integration-router.ts +++ b/packages/api/src/router/integration/integration-router.ts @@ -14,15 +14,16 @@ import { } from "@homarr/db/schema/sqlite"; import type { IntegrationSecretKind } from "@homarr/definitions"; import { + getIntegrationKindsByCategory, getPermissionsWithParents, integrationDefs, integrationKinds, integrationSecretKindObject, - isIntegrationWithSearchSupport, } from "@homarr/definitions"; -import { integrationCreatorFromSecrets } from "@homarr/integrations"; +import { integrationCreator } from "@homarr/integrations"; import { validation, z } from "@homarr/validation"; +import { createOneIntegrationMiddleware } from "../../middlewares/integration"; import { createTRPCRouter, permissionRequiredProcedure, protectedProcedure, publicProcedure } from "../../trpc"; import { throwIfActionForbiddenAsync } from "./integration-access"; import { testConnectionAsync } from "./integration-test-connection"; @@ -90,7 +91,7 @@ export const integrationRouter = createTRPCRouter({ where: inArray( integrations.kind, objectEntries(integrationDefs) - .filter(([_, integration]) => integration.supportsSearch) + .filter(([_, integration]) => [...integration.category].includes("search")) .map(([kind, _]) => kind), ), }); @@ -383,31 +384,11 @@ export const integrationRouter = createTRPCRouter({ }); }), searchInIntegration: protectedProcedure + .unstable_concat(createOneIntegrationMiddleware("query", ...getIntegrationKindsByCategory("search"))) .input(z.object({ integrationId: z.string(), query: z.string() })) .query(async ({ ctx, input }) => { - const integration = await ctx.db.query.integrations.findFirst({ - where: eq(integrations.id, input.integrationId), - with: { - secrets: true, - }, - }); - - if (!integration) { - throw new TRPCError({ - code: "NOT_FOUND", - message: "The requested integration does not exist", - }); - } - - if (!isIntegrationWithSearchSupport(integration)) { - throw new TRPCError({ - code: "BAD_REQUEST", - message: "The requested integration does not support searching", - }); - } - - const integrationInstance = integrationCreatorFromSecrets(integration); - return await integrationInstance.searchAsync(input.query); + const integrationInstance = integrationCreator(ctx.integration); + return await integrationInstance.searchAsync(encodeURI(input.query)); }), }); diff --git a/packages/definitions/src/integration.ts b/packages/definitions/src/integration.ts index 0856fa830..1ace6b955 100644 --- a/packages/definitions/src/integration.ts +++ b/packages/definitions/src/integration.ts @@ -14,7 +14,6 @@ interface integrationDefinition { iconUrl: string; secretKinds: AtLeastOneOf; // at least one secret kind set is required category: AtLeastOneOf; - supportsSearch: boolean; } export const integrationDefs = { @@ -23,140 +22,120 @@ export const integrationDefs = { secretKinds: [["apiKey"]], iconUrl: "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/png/sabnzbd.png", category: ["downloadClient", "usenet"], - supportsSearch: false, }, nzbGet: { name: "NZBGet", secretKinds: [["username", "password"]], iconUrl: "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/png/nzbget.png", category: ["downloadClient", "usenet"], - supportsSearch: false, }, deluge: { name: "Deluge", secretKinds: [["password"]], iconUrl: "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/png/deluge.png", category: ["downloadClient", "torrent"], - supportsSearch: false, }, transmission: { name: "Transmission", secretKinds: [["username", "password"]], iconUrl: "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/png/transmission.png", category: ["downloadClient", "torrent"], - supportsSearch: false, }, qBittorrent: { name: "qBittorrent", secretKinds: [["username", "password"]], iconUrl: "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/png/qbittorrent.png", category: ["downloadClient", "torrent"], - supportsSearch: false, }, sonarr: { name: "Sonarr", secretKinds: [["apiKey"]], iconUrl: "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/png/sonarr.png", category: ["calendar"], - supportsSearch: false, }, radarr: { name: "Radarr", secretKinds: [["apiKey"]], iconUrl: "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/png/radarr.png", category: ["calendar"], - supportsSearch: false, }, lidarr: { name: "Lidarr", secretKinds: [["apiKey"]], iconUrl: "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/png/lidarr.png", category: ["calendar"], - supportsSearch: false, }, readarr: { name: "Readarr", secretKinds: [["apiKey"]], iconUrl: "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/png/readarr.png", category: ["calendar"], - supportsSearch: false, }, prowlarr: { name: "Prowlarr", secretKinds: [["apiKey"]], iconUrl: "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/png/prowlarr.png", category: ["indexerManager"], - supportsSearch: false, }, jellyfin: { name: "Jellyfin", secretKinds: [["username", "password"], ["apiKey"]], iconUrl: "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/png/jellyfin.png", category: ["mediaService"], - supportsSearch: false, }, plex: { name: "Plex", secretKinds: [["apiKey"]], iconUrl: "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/png/plex.png", category: ["mediaService"], - supportsSearch: false, }, jellyseerr: { name: "Jellyseerr", secretKinds: [["apiKey"]], iconUrl: "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/png/jellyseerr.png", - category: ["mediaSearch", "mediaRequest"], - supportsSearch: true, + category: ["mediaSearch", "mediaRequest", "search"], }, overseerr: { name: "Overseerr", secretKinds: [["apiKey"]], iconUrl: "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/png/overseerr.png", - category: ["mediaSearch", "mediaRequest"], - supportsSearch: true, + category: ["mediaSearch", "mediaRequest", "search"], }, piHole: { name: "Pi-hole", secretKinds: [["apiKey"]], iconUrl: "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/png/pi-hole.png", category: ["dnsHole"], - supportsSearch: false, }, adGuardHome: { name: "AdGuard Home", secretKinds: [["username", "password"]], iconUrl: "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/png/adguard-home.png", category: ["dnsHole"], - supportsSearch: false, }, homeAssistant: { name: "Home Assistant", secretKinds: [["apiKey"]], iconUrl: "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/png/home-assistant.png", category: ["smartHomeServer"], - supportsSearch: false, }, openmediavault: { name: "OpenMediaVault", secretKinds: [["username", "password"]], iconUrl: "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/png/openmediavault.png", category: ["healthMonitoring"], - supportsSearch: false, }, dashDot: { name: "Dash.", secretKinds: [[]], category: ["healthMonitoring"], iconUrl: "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/png/dashdot.png", - supportsSearch: false, }, tdarr: { name: "Tdarr", secretKinds: [[]], category: ["mediaTranscoding"], iconUrl: "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/png/tdarr.png", - supportsSearch: false, }, } as const satisfies Record; @@ -195,22 +174,6 @@ export type IntegrationKindByCategory = { U : never; -/** - * Checks if search is supported by the integration - * Uses a typescript guard with is to allow only integrations with search support within if statement - * @param integration integration with kind - * @returns true if the integration supports search - */ -export const isIntegrationWithSearchSupport = (integration: { - kind: IntegrationKind; -}): integration is { kind: IntegrationWithSearchSupport } => { - return integrationDefs[integration.kind].supportsSearch; -}; - -type IntegrationWithSearchSupport = { - [Key in keyof typeof integrationDefs]: true extends (typeof integrationDefs)[Key]["supportsSearch"] ? Key : never; -}[keyof typeof integrationDefs]; - export type IntegrationSecretKind = keyof typeof integrationSecretKindObject; export type IntegrationKind = keyof typeof integrationDefs; export type IntegrationCategory = @@ -225,4 +188,5 @@ export type IntegrationCategory = | "smartHomeServer" | "indexerManager" | "healthMonitoring" + | "search" | "mediaTranscoding";