diff --git a/packages/common/src/test/url.spec.ts b/packages/common/src/test/url.spec.ts new file mode 100644 index 000000000..eafee07dd --- /dev/null +++ b/packages/common/src/test/url.spec.ts @@ -0,0 +1,40 @@ +import { describe, expect, test } from "vitest"; + +import { getPortFromUrl } from "../url"; + +describe("getPortFromUrl", () => { + test.each([ + [80, "http"], + [443, "https"], + ])("should return %s for %s protocol without port", (expectedPort, protocol) => { + // Arrange + const url = new URL(`${protocol}://example.com`); + + // Act + const port = getPortFromUrl(url); + + // Assert + expect(port).toBe(expectedPort); + }); + test.each([["http"], ["https"], ["anything"]])("should return the specified port for %s protocol", (protocol) => { + // Arrange + const expectedPort = 3000; + const url = new URL(`${protocol}://example.com:${expectedPort}`); + + // Act + const port = getPortFromUrl(url); + + // Assert + expect(port).toBe(expectedPort); + }); + test("should throw an error for unsupported protocol", () => { + // Arrange + const url = new URL("ftp://example.com"); + + // Act + const act = () => getPortFromUrl(url); + + // Act & Assert + expect(act).toThrowError("Unsupported protocol: ftp:"); + }); +}); diff --git a/packages/common/src/url.ts b/packages/common/src/url.ts index 8efbb968c..0bdf322fe 100644 --- a/packages/common/src/url.ts +++ b/packages/common/src/url.ts @@ -21,3 +21,20 @@ export const extractBaseUrlFromHeaders = ( return `${protocol}://${host}`; }; + +export const getPortFromUrl = (url: URL): number => { + const port = url.port; + if (port) { + return Number(port); + } + + if (url.protocol === "https:") { + return 443; + } + + if (url.protocol === "http:") { + return 80; + } + + throw new Error(`Unsupported protocol: ${url.protocol}`); +}; diff --git a/packages/integrations/src/unifi-controller/unifi-controller-integration.ts b/packages/integrations/src/unifi-controller/unifi-controller-integration.ts index 4d5233762..1c4ed7959 100644 --- a/packages/integrations/src/unifi-controller/unifi-controller-integration.ts +++ b/packages/integrations/src/unifi-controller/unifi-controller-integration.ts @@ -1,6 +1,8 @@ import type { SiteStats } from "node-unifi"; import { Controller } from "node-unifi"; +import { getPortFromUrl } from "@homarr/common"; + import { Integration } from "../base/integration"; import type { NetworkControllerSummaryIntegration } from "../interfaces/network-controller-summary/network-controller-summary-integration"; import type { NetworkControllerSummary } from "../interfaces/network-controller-summary/network-controller-summary-types"; @@ -42,20 +44,16 @@ export class UnifiControllerIntegration extends Integration implements NetworkCo } private async createControllerClientAsync() { - const portString = new URL(this.integration.url).port; - const port = Number.isInteger(portString) ? Number(portString) : undefined; - const hostname = new URL(this.integration.url).hostname; + const url = new URL(this.integration.url); const client = new Controller({ - host: hostname, - // @ts-expect-error the URL construction is incorrect and does not append the required / at the end: https://github.com/jens-maus/node-unifi/blob/05665e8f82a900a15a9ea8b1071750b29825b3bc/unifi.js#L56, https://github.com/jens-maus/node-unifi/blob/05665e8f82a900a15a9ea8b1071750b29825b3bc/unifi.js#L95 - port: port === undefined ? "/" : `${port}/`, + host: url.hostname, + port: getPortFromUrl(url), sslverify: false, // TODO: implement a "ignore certificate toggle", see https://github.com/homarr-labs/homarr/issues/2553 username: this.getSecretValue("username"), password: this.getSecretValue("password"), }); - // Object.defineProperty(client, '_baseurl', { value: url }); await client.login(this.getSecretValue("username"), this.getSecretValue("password"), null); return client; }