From 629f3b0955b90cd42f7265fb943872357db5e188 Mon Sep 17 00:00:00 2001 From: Manuel <30572287+manuel-rw@users.noreply.github.com> Date: Sat, 27 Jun 2026 15:48:05 +0200 Subject: [PATCH] feat: add dawarich integration --- packages/definitions/src/integration.ts | 8 +++ packages/integrations/src/base/creator.ts | 2 + .../src/dawarich/dawarich-integration.ts | 53 +++++++++++++++++++ .../src/dawarich/dawarich-schemas.ts | 30 +++++++++++ 4 files changed, 93 insertions(+) create mode 100644 packages/integrations/src/dawarich/dawarich-integration.ts create mode 100644 packages/integrations/src/dawarich/dawarich-schemas.ts diff --git a/packages/definitions/src/integration.ts b/packages/definitions/src/integration.ts index d6c3a2f58..12015a4bb 100644 --- a/packages/definitions/src/integration.ts +++ b/packages/definitions/src/integration.ts @@ -504,6 +504,13 @@ export const integrationDefs = { defaultUrl: "http://localhost:8001", documentationUrl: createDocumentationLink("/docs/integrations/archiveteam-warrior"), }, + dawarich: { + name: "Dawarch", + secretKinds: [["apiKey"]], + iconUrl: "https://cdn.jsdelivr.net/gh/selfhst/icons/png/dawarich.png", + category: ["geolocationTracking"], + documentationUrl: createDocumentationLink("/docs/integrations/dawarich") + }, // This integration only returns mock data, it is used during development (but can also be used in production by directly going to the create page) mock: { name: "Mock", @@ -617,6 +624,7 @@ export const integrationCategories = [ "documents", "mediaLibrary", "uptimeMonitoring", + "geolocationTracking" ] as const; export type IntegrationCategory = (typeof integrationCategories)[number]; diff --git a/packages/integrations/src/base/creator.ts b/packages/integrations/src/base/creator.ts index 13ed36537..f49f851d4 100644 --- a/packages/integrations/src/base/creator.ts +++ b/packages/integrations/src/base/creator.ts @@ -60,6 +60,7 @@ import { UptimeKumaIntegration } from "../uptime-kuma/uptime-kuma-integration"; import { UnifiControllerIntegration } from "../unifi-controller/unifi-controller-integration"; import { UnraidIntegration } from "../unraid/unraid-integration"; import type { Integration, IntegrationInput } from "./integration"; +import {DawarichIntegration} from "../dawarich/dawarich-integration"; export const createIntegrationAsync = async ( integration: IntegrationInput & { kind: TKind }, @@ -143,6 +144,7 @@ export const integrationCreators = { uptimeKuma: UptimeKumaIntegration, peaNut: PeaNutIntegration, beszel: BeszelIntegration, + dawarich: DawarichIntegration } satisfies Record Promise]>; type IntegrationInstanceOfKind = { diff --git a/packages/integrations/src/dawarich/dawarich-integration.ts b/packages/integrations/src/dawarich/dawarich-integration.ts new file mode 100644 index 000000000..d116290ed --- /dev/null +++ b/packages/integrations/src/dawarich/dawarich-integration.ts @@ -0,0 +1,53 @@ +import {fetchWithTrustedCertificatesAsync} from "@homarr/core/infrastructure/http"; +import {ResponseError} from "@homarr/common/server"; +import {Integration, IntegrationInput, IntegrationTestingInput} from "../base/integration"; +import {dawarichPlacesSchema, dawarichStatisticsSchema} from "./dawarich-schemas"; +import {z} from "zod/v4"; +import {TestingResult} from "../base/test-connection/test-connection-service"; +import { TestConnectionError } from "../base/test-connection/test-connection-error"; + +export class DawarichIntegration extends Integration { + constructor(integration: IntegrationInput) { + super(integration); + } + + protected async testingAsync(input: IntegrationTestingInput): Promise { + const response = await fetchWithTrustedCertificatesAsync(this.url(`/api/v1/users/me`), { + headers: {Authorization: `Bearer ${this.getSecretValue('apiKey')}`}, + }); + + if (response.status === 200) { + return { success: true }; + } + + return TestConnectionError.StatusResult(response); + } + + async getStatisticsAsync(): Promise<{ + totalDistanceKm: number, + totalPointsTracked: number, + totalReverseGeocodedPoints: number, + totalCountriesVisited: number, + totalCitiesVisited: number + }> { + const response = await fetchWithTrustedCertificatesAsync(this.url('/api/v1/stats'), { + headers: {Authorization: `Bearer ${this.getSecretValue('apiKey')}`}, + }); + + if (!response.ok) + throw new ResponseError(response); + + return dawarichStatisticsSchema.parseAsync(await response.json()); + } + + async getPlacesAsync(): Promise> { + const response = await fetchWithTrustedCertificatesAsync(this.url('/api/v1/places'), { + headers: {Authorization: `Bearer ${this.getSecretValue('apiKey')}`}, + }); + + if (!response.ok) + throw new ResponseError(response); + + return dawarichPlacesSchema.parseAsync(await response.json()); + } +} \ No newline at end of file diff --git a/packages/integrations/src/dawarich/dawarich-schemas.ts b/packages/integrations/src/dawarich/dawarich-schemas.ts new file mode 100644 index 000000000..0557ab88b --- /dev/null +++ b/packages/integrations/src/dawarich/dawarich-schemas.ts @@ -0,0 +1,30 @@ +import {z} from "zod/v4"; + +export const dawarichStatisticsSchema = z.object({ + "totalDistanceKm": z.number(), + "totalPointsTracked": z.number(), + "totalReverseGeocodedPoints": z.number(), + "totalCountriesVisited": z.number(), + "totalCitiesVisited": z.number() +}); + +export const dawarichPlacesSchema = z.array(z.object({ + + "id": z.number(), + "name": z.string(), + "latitude": z.number(), + "longitude": z.number(), + "source": z.string().optional(), + "icon": z.string().nullable(), + "color": z.string().nullable(), + "visits_count": z.number(), + "created_at": "2024-07-29T15:51:28.071Z", + "tags": [ + { + "id": z.number(), + "name": z.string(), + "icon": z.string(), + "color": z.string() + } + ] +})); \ No newline at end of file