diff --git a/apps/docs/docs/integrations/whats-up-docker/index.mdx b/apps/docs/docs/integrations/whats-up-docker/index.mdx index 31a0127f5..85707365e 100644 --- a/apps/docs/docs/integrations/whats-up-docker/index.mdx +++ b/apps/docs/docs/integrations/whats-up-docker/index.mdx @@ -24,12 +24,14 @@ The What's Up Docker integration lets Homarr connect to your [WUD](https://githu :::info URL configuration -Use the root URL of your WUD instance, for example `https://wud.example.com`. Homarr calls the `/api/app` and `/api/containers` endpoints under that URL automatically. +Use the root URL of your WUD instance, for example `https://wud.example.com`. Homarr uses `/api/containers` under that URL for both connection testing and widget data. ::: ### Secrets -WUD does not require authentication by default. If your instance is protected with [Basic Authentication](https://github.com/getwud/wud/blob/main/docs/configuration/authentications/basic/README.md), you can provide a username and password. +WUD 9 requires authentication. Enter your WUD username and password in Homarr; the initial admin credentials configured with `WUD_AUTH_ADMIN_USER` and `WUD_AUTH_ADMIN_PASSWORD` work with Basic authentication. Older WUD versions without authentication can leave both fields empty. + +Credentials are sent to the configured URL over HTTP or HTTPS. Use HTTPS outside a trusted internal network. { expect(requestInit?.timeout).toBe(10_000); }); - test("does not send credentials over a non-HTTPS URL", async () => { + test("sends configured Basic auth credentials over an HTTP URL", async () => { mockFetch.mockResolvedValue( new Response(JSON.stringify(sampleContainersResponse), { status: 200, @@ -209,7 +209,7 @@ describe("WudIntegration authentication", () => { const integration = new WudIntegration({ id: "test-wud-http", name: "Test WUD HTTP", - url: "http://wud.example.com", + url: "http://wud:3000", externalUrl: null, decryptedSecrets: [ { kind: "username", value: TEST_USERNAME }, @@ -221,7 +221,7 @@ describe("WudIntegration authentication", () => { const [, requestInit] = mockFetch.mock.calls[0] ?? []; const headers = (requestInit?.headers ?? {}) as Record; - expect(headers.Authorization).toBeUndefined(); + expect(headers.Authorization).toBe(`Basic ${Buffer.from(`${TEST_USERNAME}:${TEST_PASSWORD}`).toString("base64")}`); }); }); @@ -238,15 +238,16 @@ describe("WudIntegration testing endpoint", () => { fetchAsync: vi.fn().mockResolvedValue(response), }) as unknown as IntegrationTestingInput; - test("returns success when /api/app responds 200", async () => { - const input = makeInput(new Response(JSON.stringify({ name: "wud", version: "5.0.0" }), { status: 200 })); + test("returns success when /api/containers responds 200", async () => { + const input = makeInput(new Response(JSON.stringify(sampleContainersResponse), { status: 200 })); const result = await invokeTesting(createIntegration(), input); expect(result.success).toBe(true); + expect(String(vi.mocked(input.fetchAsync).mock.calls[0]?.[0])).toBe(`${TEST_URL}/api/containers`); }); - test("returns a status-code failure when /api/app responds non-OK", async () => { + test("returns a status-code failure when /api/containers responds non-OK", async () => { const input = makeInput(new Response("unauthorized", { status: 401 })); const result = await invokeTesting(createIntegration(), input); @@ -255,7 +256,7 @@ describe("WudIntegration testing endpoint", () => { }); test("passes the Basic auth header through to fetchAsync when configured", async () => { - const response = new Response(JSON.stringify({ name: "wud", version: "5.0.0" }), { status: 200 }); + const response = new Response(JSON.stringify(sampleContainersResponse), { status: 200 }); const input = makeInput(response); await invokeTesting(createIntegrationWithBasicAuth(), input); diff --git a/packages/integrations/src/wud/wud-integration.ts b/packages/integrations/src/wud/wud-integration.ts index d7bdb49eb..05c6cc17a 100644 --- a/packages/integrations/src/wud/wud-integration.ts +++ b/packages/integrations/src/wud/wud-integration.ts @@ -14,7 +14,7 @@ const CONTAINERS_REQUEST_TIMEOUT_MS = 10_000; export class WudIntegration extends Integration { protected async testingAsync(input: IntegrationTestingInput): Promise { - const response = await input.fetchAsync(this.url("/api/app"), { + const response = await input.fetchAsync(this.url("/api/containers"), { headers: this.getAuthHeaders(), }); @@ -23,10 +23,10 @@ export class WudIntegration extends Integration { } try { - await response.json(); + await parseWudContainersResponseAsync(response); } catch (error) { return TestConnectionError.ParseResult( - new ParseError("Invalid WUD app response", { + new ParseError("Invalid WUD containers response", { cause: error instanceof Error ? error : new Error(String(error)), }), ); @@ -55,10 +55,6 @@ export class WudIntegration extends Integration { return undefined; } - if (this.url("/api/app").protocol !== "https:") { - return undefined; - } - const credentials = Buffer.from(`${this.getSecretValue("username")}:${this.getSecretValue("password")}`).toString( "base64", );