diff --git a/packages/api/src/router/integration/integration-test-connection.ts b/packages/api/src/router/integration/integration-test-connection.ts index cd82b8cfa..5888b1ddc 100644 --- a/packages/api/src/router/integration/integration-test-connection.ts +++ b/packages/api/src/router/integration/integration-test-connection.ts @@ -101,6 +101,18 @@ const getSecretKindOption = (kind: IntegrationKind, sourcedSecrets: SourcedInteg ); if (onlyFormSecretsKindOptions.length >= 1) { + // If the first option is no secret it would always be selected even if we want to have a secret + if ( + onlyFormSecretsKindOptions.length >= 2 && + onlyFormSecretsKindOptions.some((secretKinds) => secretKinds.length === 0) + ) { + return ( + // Will never be undefined because of the check above + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + onlyFormSecretsKindOptions.find((secretKinds) => secretKinds.length >= 1) ?? onlyFormSecretsKindOptions[0]! + ); + } + // Will never be undefined because of the check above // eslint-disable-next-line @typescript-eslint/no-non-null-assertion return onlyFormSecretsKindOptions[0]!; diff --git a/packages/definitions/src/integration.ts b/packages/definitions/src/integration.ts index 9c0b78b21..a363a6cac 100644 --- a/packages/definitions/src/integration.ts +++ b/packages/definitions/src/integration.ts @@ -142,26 +142,26 @@ export const integrationDefs = { dashDot: { name: "Dash.", secretKinds: [[]], - category: ["healthMonitoring"], iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/png/dashdot.png", + category: ["healthMonitoring"], }, tdarr: { name: "Tdarr", - secretKinds: [[]], - category: ["mediaTranscoding"], + secretKinds: [[], ["apiKey"]], iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/png/tdarr.png", + category: ["mediaTranscoding"], }, proxmox: { name: "Proxmox", secretKinds: [["username", "tokenId", "apiKey", "realm"]], - category: ["healthMonitoring"], iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/proxmox.svg", + category: ["healthMonitoring"], }, nextcloud: { name: "Nextcloud", secretKinds: [["username", "password"]], - category: ["calendar"], iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/nextcloud.svg", + category: ["calendar"], }, unifiController: { name: "Unifi Controller", diff --git a/packages/integrations/src/media-transcoding/tdarr-integration.ts b/packages/integrations/src/media-transcoding/tdarr-integration.ts index 15a47076f..5e39d74c9 100644 --- a/packages/integrations/src/media-transcoding/tdarr-integration.ts +++ b/packages/integrations/src/media-transcoding/tdarr-integration.ts @@ -1,5 +1,3 @@ -import { z } from "zod"; - import { fetchWithTrustedCertificatesAsync } from "@homarr/certificates/server"; import { Integration } from "../base/integration"; @@ -10,20 +8,31 @@ import { getNodesResponseSchema, getStatisticsSchema, getStatusTableSchema } fro export class TdarrIntegration extends Integration { public async testConnectionAsync(): Promise { - const url = this.url("/api/v2/status"); - const response = await fetchWithTrustedCertificatesAsync(url); - if (response.status !== 200) { - throw new Error(`Unexpected status code: ${response.status}`); - } - - await z.object({ status: z.string() }).parseAsync(await response.json()); + await super.handleTestConnectionResponseAsync({ + queryFunctionAsync: async () => { + return await fetchWithTrustedCertificatesAsync(this.url("/api/v2/is-server-alive"), { + method: "POST", + headers: { + accept: "application/json", + "X-Api-Key": super.hasSecretValue("apiKey") ? super.getSecretValue("apiKey") : "", + }, + }); + }, + }); } public async getStatisticsAsync(): Promise { const url = this.url("/api/v2/stats/get-pies"); + + const headerParams = { + accept: "application/json", + "Content-Type": "application/json", + ...(super.hasSecretValue("apiKey") ? { "X-Api-Key": super.getSecretValue("apiKey") } : {}), + }; + const response = await fetchWithTrustedCertificatesAsync(url, { method: "POST", - headers: { accept: "application/json", "Content-Type": "application/json" }, + headers: headerParams, body: JSON.stringify({ data: { libraryId: "", // empty string to get all libraries @@ -62,9 +71,13 @@ export class TdarrIntegration extends Integration { public async getWorkersAsync(): Promise { const url = this.url("/api/v2/get-nodes"); + const headerParams = { + "Content-Type": "application/json", + ...(super.hasSecretValue("apiKey") ? { "X-Api-Key": super.getSecretValue("apiKey") } : {}), + }; const response = await fetchWithTrustedCertificatesAsync(url, { method: "GET", - headers: { "content-type": "application/json" }, + headers: headerParams, }); const nodesData = await getNodesResponseSchema.parseAsync(await response.json()); @@ -102,9 +115,13 @@ export class TdarrIntegration extends Integration { private async getTranscodingQueueAsync(firstItemIndex: number, pageSize: number) { const url = this.url("/api/v2/client/status-tables"); + const headerParams = { + "Content-Type": "application/json", + ...(super.hasSecretValue("apiKey") ? { "X-Api-Key": super.getSecretValue("apiKey") } : {}), + }; const response = await fetchWithTrustedCertificatesAsync(url, { method: "POST", - headers: { "content-type": "application/json" }, + headers: headerParams, body: JSON.stringify({ data: { start: firstItemIndex, @@ -137,9 +154,13 @@ export class TdarrIntegration extends Integration { private async getHealthCheckDataAsync(firstItemIndex: number, pageSize: number, totalQueueCount: number) { const url = this.url("/api/v2/client/status-tables"); + const headerParams = { + "Content-Type": "application/json", + ...(super.hasSecretValue("apiKey") ? { "X-Api-Key": super.getSecretValue("apiKey") } : {}), + }; const response = await fetchWithTrustedCertificatesAsync(url, { method: "POST", - headers: { "content-type": "application/json" }, + headers: headerParams, body: JSON.stringify({ data: { start: Math.max(firstItemIndex - totalQueueCount, 0),