feat: expand advanced widget experiences
This commit is contained in:
1 parent
55226842f9
commit
65a7e84bea
137 files changed
+3684
-850
No files matched your search
@@ -0,0 +1,68 @@
|
||||
import { describe, expect, test } from "vitest";
|
||||
|
||||
import type { MediaRelease } from "@homarr/integrations/types";
|
||||
|
||||
import { PUBLIC_INTEGRATION_ERROR } from "../../../settle-integrations";
|
||||
import { getMediaReleaseResponse } from "../media-release-result";
|
||||
|
||||
const integrations = [
|
||||
{ id: "plex", name: "Living room Plex", kind: "plex" as const },
|
||||
{ id: "jellyfin", name: "Family Jellyfin", kind: "jellyfin" as const },
|
||||
];
|
||||
|
||||
const release: MediaRelease = {
|
||||
id: "shared-id",
|
||||
type: "movie",
|
||||
title: "Example release",
|
||||
releaseDate: new Date("2026-08-13T10:00:00Z"),
|
||||
imageUrls: { poster: undefined, backdrop: undefined },
|
||||
tags: [],
|
||||
href: "https://example.com/release",
|
||||
};
|
||||
|
||||
describe("getMediaReleaseResponse", () => {
|
||||
test("keeps healthy releases and returns a sanitized failure envelope", async () => {
|
||||
const response = await getMediaReleaseResponse(integrations, async (integration) => {
|
||||
if (integration.id === "plex") throw new Error("token=secret at plex.internal");
|
||||
return { releases: [release], updatedAt: new Date("2026-08-13T11:00:00Z") };
|
||||
});
|
||||
|
||||
expect(response.releases).toEqual([
|
||||
{
|
||||
...release,
|
||||
integration: {
|
||||
...integrations[1],
|
||||
updatedAt: new Date("2026-08-13T11:00:00Z"),
|
||||
},
|
||||
},
|
||||
]);
|
||||
expect(response.failedIntegrations).toEqual([
|
||||
{
|
||||
integrationId: "plex",
|
||||
integrationName: "Living room Plex",
|
||||
error: PUBLIC_INTEGRATION_ERROR,
|
||||
},
|
||||
]);
|
||||
expect(JSON.stringify(response)).not.toMatch(/secret|plex\.internal/i);
|
||||
});
|
||||
|
||||
test("keeps source identity when integrations return the same release id", async () => {
|
||||
const response = await getMediaReleaseResponse(integrations, async () => ({
|
||||
releases: [release],
|
||||
updatedAt: new Date("2026-08-13T11:00:00Z"),
|
||||
}));
|
||||
|
||||
expect(response.releases.map(({ integration }) => integration.id)).toEqual(["plex", "jellyfin"]);
|
||||
});
|
||||
|
||||
test("throws when every integration fails", async () => {
|
||||
await expect(
|
||||
getMediaReleaseResponse(integrations, async () => {
|
||||
throw new Error("Unavailable");
|
||||
}),
|
||||
).rejects.toMatchObject({
|
||||
code: "INTERNAL_SERVER_ERROR",
|
||||
message: "All integration queries failed",
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,24 +1,48 @@
|
||||
import type { CoolifyInstanceInfo } from "@homarr/integrations/types";
|
||||
import { coolifyRequestHandler } from "@homarr/request-handler/coolify";
|
||||
|
||||
import { createManyIntegrationMiddleware } from "../../middlewares/integration";
|
||||
import { settleIntegrationQueries } from "../../settle-integrations";
|
||||
import { settleIntegrationQueries, toPublicIntegrationError } from "../../settle-integrations";
|
||||
import { createTRPCRouter, publicProcedure } from "../../trpc";
|
||||
|
||||
interface CoolifyInstanceResult {
|
||||
integrationId: string;
|
||||
integrationName: string;
|
||||
integrationUrl: string;
|
||||
instanceInfo: CoolifyInstanceInfo | null;
|
||||
updatedAt: Date;
|
||||
error?: string;
|
||||
}
|
||||
|
||||
export const coolifyRouter = createTRPCRouter({
|
||||
getInstancesInfo: publicProcedure
|
||||
.concat(createManyIntegrationMiddleware("query", "coolify"))
|
||||
.query(async ({ ctx }) => {
|
||||
return await settleIntegrationQueries(ctx.integrations, async (integration) => {
|
||||
const innerHandler = coolifyRequestHandler.handler(integration, {});
|
||||
const { data, timestamp } = await innerHandler.getDataAsync();
|
||||
return await settleIntegrationQueries(
|
||||
ctx.integrations,
|
||||
async (integration): Promise<CoolifyInstanceResult> => {
|
||||
const innerHandler = coolifyRequestHandler.handler(integration, {});
|
||||
const { data, timestamp } = await innerHandler.getDataAsync();
|
||||
|
||||
return {
|
||||
integrationId: integration.id,
|
||||
integrationName: integration.name,
|
||||
integrationUrl: integration.url,
|
||||
instanceInfo: data,
|
||||
updatedAt: timestamp,
|
||||
};
|
||||
});
|
||||
return {
|
||||
integrationId: integration.id,
|
||||
integrationName: integration.name,
|
||||
integrationUrl: integration.url,
|
||||
instanceInfo: data,
|
||||
updatedAt: timestamp,
|
||||
};
|
||||
},
|
||||
{
|
||||
fallback: (integration, error): CoolifyInstanceResult => ({
|
||||
integrationId: integration.id,
|
||||
integrationName: integration.name,
|
||||
integrationUrl: integration.url,
|
||||
instanceInfo: null,
|
||||
updatedAt: new Date(0),
|
||||
error: toPublicIntegrationError(error),
|
||||
}),
|
||||
throwOnAllFailures: true,
|
||||
},
|
||||
);
|
||||
}),
|
||||
});
|
||||
@@ -1,13 +1,28 @@
|
||||
import { z } from "zod/v4";
|
||||
|
||||
import type { IntegrationKindByCategory } from "@homarr/definitions";
|
||||
import { getIntegrationKindsByCategory } from "@homarr/definitions";
|
||||
import { createIntegrationAsync } from "@homarr/integrations";
|
||||
import type { DnsHoleSummary } from "@homarr/integrations/types";
|
||||
import { dnsHoleRequestHandler } from "@homarr/request-handler/dns-hole";
|
||||
|
||||
import { createManyIntegrationMiddleware, createOneIntegrationMiddleware } from "../../middlewares/integration";
|
||||
import { settleIntegrationQueries } from "../../settle-integrations";
|
||||
import { PUBLIC_INTEGRATION_ERROR, settleIntegrationQueries } from "../../settle-integrations";
|
||||
import { createTRPCRouter, protectedProcedure, publicProcedure } from "../../trpc";
|
||||
|
||||
interface DnsHoleSummaryResult {
|
||||
integrationId: string;
|
||||
integrationName: string;
|
||||
integration: {
|
||||
id: string;
|
||||
name: string;
|
||||
kind: IntegrationKindByCategory<"dnsHole">;
|
||||
updatedAt?: Date;
|
||||
};
|
||||
summary: DnsHoleSummary | null;
|
||||
error?: string;
|
||||
}
|
||||
|
||||
export const dnsHoleRouter = createTRPCRouter({
|
||||
summary: publicProcedure
|
||||
.meta({
|
||||
@@ -19,13 +34,28 @@ export const dnsHoleRouter = createTRPCRouter({
|
||||
})
|
||||
.concat(createManyIntegrationMiddleware("query", ...getIntegrationKindsByCategory("dnsHole")))
|
||||
.query(async ({ ctx }) => {
|
||||
return await settleIntegrationQueries(ctx.integrations, async (integration) => {
|
||||
const { data, timestamp } = await dnsHoleRequestHandler.handler(integration, {}).getDataAsync();
|
||||
return {
|
||||
integration: { id: integration.id, name: integration.name, kind: integration.kind, updatedAt: timestamp },
|
||||
summary: data,
|
||||
};
|
||||
});
|
||||
return await settleIntegrationQueries<(typeof ctx.integrations)[number], DnsHoleSummaryResult>(
|
||||
ctx.integrations,
|
||||
async (integration) => {
|
||||
const { data, timestamp } = await dnsHoleRequestHandler.handler(integration, {}).getDataAsync();
|
||||
return {
|
||||
integrationId: integration.id,
|
||||
integrationName: integration.name,
|
||||
integration: { id: integration.id, name: integration.name, kind: integration.kind, updatedAt: timestamp },
|
||||
summary: data,
|
||||
};
|
||||
},
|
||||
{
|
||||
fallback: (integration) => ({
|
||||
integrationId: integration.id,
|
||||
integrationName: integration.name,
|
||||
integration: { id: integration.id, name: integration.name, kind: integration.kind },
|
||||
summary: null,
|
||||
error: PUBLIC_INTEGRATION_ERROR,
|
||||
}),
|
||||
throwOnAllFailures: true,
|
||||
},
|
||||
);
|
||||
}),
|
||||
|
||||
enable: protectedProcedure
|
||||
|
||||
@@ -1,17 +1,47 @@
|
||||
import { z } from "zod/v4";
|
||||
|
||||
import type { IntegrationKindByCategory } from "@homarr/definitions";
|
||||
import { getIntegrationKindsByCategory } from "@homarr/definitions";
|
||||
import type { DownloadClientJobsAndStatus } from "@homarr/integrations";
|
||||
import { createIntegrationAsync, downloadClientItemSchema } from "@homarr/integrations";
|
||||
import { downloadClientRequestHandler } from "@homarr/request-handler/downloads";
|
||||
|
||||
import type { IntegrationAction } from "../../middlewares/integration";
|
||||
import { createManyIntegrationMiddleware } from "../../middlewares/integration";
|
||||
import { settleIntegrationQueries } from "../../settle-integrations";
|
||||
import { PUBLIC_INTEGRATION_ERROR, settleIntegrationQueries } from "../../settle-integrations";
|
||||
import { createTRPCRouter, protectedProcedure, publicProcedure } from "../../trpc";
|
||||
|
||||
const createDownloadClientIntegrationMiddleware = (action: IntegrationAction) =>
|
||||
createManyIntegrationMiddleware(action, ...getIntegrationKindsByCategory("downloadClient"));
|
||||
|
||||
interface DownloadClientQueryResultBase {
|
||||
integrationId: string;
|
||||
integrationName: string;
|
||||
}
|
||||
|
||||
type DownloadClientQueryResult = DownloadClientQueryResultBase &
|
||||
(
|
||||
| {
|
||||
integration: {
|
||||
id: string;
|
||||
name: string;
|
||||
kind: IntegrationKindByCategory<"downloadClient">;
|
||||
updatedAt: Date;
|
||||
};
|
||||
data: DownloadClientJobsAndStatus;
|
||||
error?: never;
|
||||
}
|
||||
| {
|
||||
integration: {
|
||||
id: string;
|
||||
name: string;
|
||||
kind: IntegrationKindByCategory<"downloadClient">;
|
||||
};
|
||||
data: null;
|
||||
error: string;
|
||||
}
|
||||
);
|
||||
|
||||
export const downloadsRouter = createTRPCRouter({
|
||||
getJobsAndStatuses: publicProcedure
|
||||
.meta({
|
||||
@@ -24,14 +54,29 @@ export const downloadsRouter = createTRPCRouter({
|
||||
.concat(createDownloadClientIntegrationMiddleware("query"))
|
||||
.input(z.object({ limitPerIntegration: z.number().default(50) }))
|
||||
.query(async ({ ctx, input }) => {
|
||||
return await settleIntegrationQueries(ctx.integrations, async (integration) => {
|
||||
const innerHandler = downloadClientRequestHandler.handler(integration, { limit: input.limitPerIntegration });
|
||||
const { data, timestamp } = await innerHandler.getDataAsync();
|
||||
return {
|
||||
integration: { id: integration.id, name: integration.name, kind: integration.kind, updatedAt: timestamp },
|
||||
data,
|
||||
};
|
||||
});
|
||||
return await settleIntegrationQueries<(typeof ctx.integrations)[number], DownloadClientQueryResult>(
|
||||
ctx.integrations,
|
||||
async (integration) => {
|
||||
const innerHandler = downloadClientRequestHandler.handler(integration, { limit: input.limitPerIntegration });
|
||||
const { data, timestamp } = await innerHandler.getDataAsync();
|
||||
return {
|
||||
integrationId: integration.id,
|
||||
integrationName: integration.name,
|
||||
integration: { id: integration.id, name: integration.name, kind: integration.kind, updatedAt: timestamp },
|
||||
data,
|
||||
};
|
||||
},
|
||||
{
|
||||
fallback: (integration) => ({
|
||||
integrationId: integration.id,
|
||||
integrationName: integration.name,
|
||||
integration: { id: integration.id, name: integration.name, kind: integration.kind },
|
||||
data: null,
|
||||
error: PUBLIC_INTEGRATION_ERROR,
|
||||
}),
|
||||
throwOnAllFailures: true,
|
||||
},
|
||||
);
|
||||
}),
|
||||
pause: protectedProcedure
|
||||
.meta({
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import { createIntegrationAsync } from "@homarr/integrations";
|
||||
import type { SystemHealthMonitoring } from "@homarr/integrations";
|
||||
import { SynologyIntegration } from "@homarr/integrations";
|
||||
import { clusterInfoRequestHandler, systemInfoRequestHandler } from "@homarr/request-handler/health-monitoring";
|
||||
|
||||
import { createManyIntegrationMiddleware, createOneIntegrationMiddleware } from "../../middlewares/integration";
|
||||
import { settleIntegrationQueries } from "../../settle-integrations";
|
||||
import { PUBLIC_INTEGRATION_ERROR, settleIntegrationQueries } from "../../settle-integrations";
|
||||
import { createTRPCRouter, publicProcedure } from "../../trpc";
|
||||
|
||||
const healthMonitoringIntegrationKinds = [
|
||||
@@ -16,6 +17,14 @@ const healthMonitoringIntegrationKinds = [
|
||||
"mock",
|
||||
] as const;
|
||||
|
||||
interface SystemHealthQueryResult {
|
||||
integrationId: string;
|
||||
integrationName: string;
|
||||
healthInfo: SystemHealthMonitoring | null;
|
||||
updatedAt?: Date;
|
||||
error?: string;
|
||||
}
|
||||
|
||||
export const healthMonitoringRouter = createTRPCRouter({
|
||||
getSystemHealthStatus: publicProcedure
|
||||
.meta({
|
||||
@@ -27,15 +36,27 @@ export const healthMonitoringRouter = createTRPCRouter({
|
||||
})
|
||||
.concat(createManyIntegrationMiddleware("query", ...healthMonitoringIntegrationKinds))
|
||||
.query(async ({ ctx }) => {
|
||||
return await settleIntegrationQueries(ctx.integrations, async (integration) => {
|
||||
const { data, timestamp } = await systemInfoRequestHandler.handler(integration, {}).getDataAsync();
|
||||
return {
|
||||
integrationId: integration.id,
|
||||
integrationName: integration.name,
|
||||
healthInfo: data,
|
||||
updatedAt: timestamp,
|
||||
};
|
||||
});
|
||||
return await settleIntegrationQueries<(typeof ctx.integrations)[number], SystemHealthQueryResult>(
|
||||
ctx.integrations,
|
||||
async (integration) => {
|
||||
const { data, timestamp } = await systemInfoRequestHandler.handler(integration, {}).getDataAsync();
|
||||
return {
|
||||
integrationId: integration.id,
|
||||
integrationName: integration.name,
|
||||
healthInfo: data,
|
||||
updatedAt: timestamp,
|
||||
};
|
||||
},
|
||||
{
|
||||
fallback: (integration) => ({
|
||||
integrationId: integration.id,
|
||||
integrationName: integration.name,
|
||||
healthInfo: null,
|
||||
error: PUBLIC_INTEGRATION_ERROR,
|
||||
}),
|
||||
throwOnAllFailures: true,
|
||||
},
|
||||
);
|
||||
}),
|
||||
listStorageVolumes: publicProcedure
|
||||
.meta({
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
import type { IntegrationKindByCategory } from "@homarr/definitions";
|
||||
import type { MediaRelease } from "@homarr/integrations/types";
|
||||
|
||||
import { PUBLIC_INTEGRATION_ERROR, settleIntegrationQueries } from "../../settle-integrations";
|
||||
|
||||
interface MediaReleaseSource {
|
||||
id: string;
|
||||
name: string;
|
||||
kind: IntegrationKindByCategory<"mediaRelease">;
|
||||
}
|
||||
|
||||
interface MediaReleaseLoadResult {
|
||||
releases: MediaRelease[];
|
||||
updatedAt: Date;
|
||||
}
|
||||
|
||||
interface MediaReleaseIntegrationResult {
|
||||
integration: MediaReleaseSource & { updatedAt?: Date };
|
||||
releases: MediaRelease[];
|
||||
error?: string;
|
||||
}
|
||||
|
||||
export async function getMediaReleaseResponse<TIntegration extends MediaReleaseSource>(
|
||||
integrations: TIntegration[],
|
||||
load: (integration: TIntegration) => Promise<MediaReleaseLoadResult>,
|
||||
) {
|
||||
const results = await settleIntegrationQueries<TIntegration, MediaReleaseIntegrationResult>(
|
||||
integrations,
|
||||
async (integration) => {
|
||||
const { releases, updatedAt } = await load(integration);
|
||||
return {
|
||||
integration: {
|
||||
id: integration.id,
|
||||
name: integration.name,
|
||||
kind: integration.kind,
|
||||
updatedAt,
|
||||
},
|
||||
releases,
|
||||
};
|
||||
},
|
||||
{
|
||||
fallback: (integration) => ({
|
||||
integration: {
|
||||
id: integration.id,
|
||||
name: integration.name,
|
||||
kind: integration.kind,
|
||||
},
|
||||
releases: [],
|
||||
error: PUBLIC_INTEGRATION_ERROR,
|
||||
}),
|
||||
throwOnAllFailures: true,
|
||||
},
|
||||
);
|
||||
|
||||
return {
|
||||
releases: results.flatMap(({ integration, releases }) => releases.map((release) => ({ ...release, integration }))),
|
||||
failedIntegrations: results.flatMap(({ integration, error }) =>
|
||||
error ? [{ integrationId: integration.id, integrationName: integration.name, error }] : [],
|
||||
),
|
||||
};
|
||||
}
|
||||
@@ -2,32 +2,21 @@ import { getIntegrationKindsByCategory } from "@homarr/definitions";
|
||||
import { mediaReleaseRequestHandler } from "@homarr/request-handler/media-release";
|
||||
|
||||
import { createManyIntegrationMiddleware } from "../../middlewares/integration";
|
||||
import { settleIntegrationQueries } from "../../settle-integrations";
|
||||
import { createTRPCRouter, publicProcedure } from "../../trpc";
|
||||
import { getMediaReleaseResponse } from "./media-release-result";
|
||||
|
||||
export const mediaReleaseRouter = createTRPCRouter({
|
||||
getMediaReleases: publicProcedure
|
||||
.concat(createManyIntegrationMiddleware("query", ...getIntegrationKindsByCategory("mediaRelease")))
|
||||
.query(async ({ ctx }) => {
|
||||
const results = await settleIntegrationQueries(ctx.integrations, async (integration) => {
|
||||
return await getMediaReleaseResponse(ctx.integrations, async (integration) => {
|
||||
const innerHandler = mediaReleaseRequestHandler.handler(integration, {});
|
||||
const { data, timestamp } = await innerHandler.getDataAsync();
|
||||
|
||||
return {
|
||||
integration: {
|
||||
id: integration.id,
|
||||
name: integration.name,
|
||||
kind: integration.kind,
|
||||
updatedAt: timestamp,
|
||||
},
|
||||
releases: data,
|
||||
updatedAt: timestamp,
|
||||
};
|
||||
});
|
||||
return results.flatMap((result) =>
|
||||
result.releases.map((release) => ({
|
||||
...release,
|
||||
integration: result.integration,
|
||||
})),
|
||||
);
|
||||
}),
|
||||
});
|
||||
@@ -1,27 +1,53 @@
|
||||
import type { IntegrationKindByCategory } from "@homarr/definitions";
|
||||
import { getIntegrationKindsByCategory } from "@homarr/definitions";
|
||||
import type { NetworkControllerSummary } from "@homarr/integrations/types";
|
||||
import { networkControllerRequestHandler } from "@homarr/request-handler/network-controller";
|
||||
|
||||
import { createManyIntegrationMiddleware } from "../../middlewares/integration";
|
||||
import { settleIntegrationQueries } from "../../settle-integrations";
|
||||
import { PUBLIC_INTEGRATION_ERROR, settleIntegrationQueries } from "../../settle-integrations";
|
||||
import { createTRPCRouter, publicProcedure } from "../../trpc";
|
||||
|
||||
interface NetworkControllerQueryResult {
|
||||
integrationId: string;
|
||||
integrationName: string;
|
||||
integration: {
|
||||
id: string;
|
||||
name: string;
|
||||
kind: IntegrationKindByCategory<"networkController">;
|
||||
};
|
||||
summary: NetworkControllerSummary | null;
|
||||
updatedAt?: Date;
|
||||
error?: string;
|
||||
}
|
||||
|
||||
export const networkControllerRouter = createTRPCRouter({
|
||||
summary: publicProcedure
|
||||
.concat(createManyIntegrationMiddleware("query", ...getIntegrationKindsByCategory("networkController")))
|
||||
.query(async ({ ctx }) => {
|
||||
return await settleIntegrationQueries(ctx.integrations, async (integration) => {
|
||||
const innerHandler = networkControllerRequestHandler.handler(integration, {});
|
||||
const { data, timestamp } = await innerHandler.getDataAsync();
|
||||
return await settleIntegrationQueries<(typeof ctx.integrations)[number], NetworkControllerQueryResult>(
|
||||
ctx.integrations,
|
||||
async (integration) => {
|
||||
const innerHandler = networkControllerRequestHandler.handler(integration, {});
|
||||
const { data, timestamp } = await innerHandler.getDataAsync();
|
||||
|
||||
return {
|
||||
integration: {
|
||||
id: integration.id,
|
||||
name: integration.name,
|
||||
kind: integration.kind,
|
||||
},
|
||||
summary: data,
|
||||
updatedAt: timestamp,
|
||||
};
|
||||
});
|
||||
return {
|
||||
integrationId: integration.id,
|
||||
integrationName: integration.name,
|
||||
integration: { id: integration.id, name: integration.name, kind: integration.kind },
|
||||
summary: data,
|
||||
updatedAt: timestamp,
|
||||
};
|
||||
},
|
||||
{
|
||||
fallback: (integration) => ({
|
||||
integrationId: integration.id,
|
||||
integrationName: integration.name,
|
||||
integration: { id: integration.id, name: integration.name, kind: integration.kind },
|
||||
summary: null,
|
||||
error: PUBLIC_INTEGRATION_ERROR,
|
||||
}),
|
||||
throwOnAllFailures: true,
|
||||
},
|
||||
);
|
||||
}),
|
||||
});
|
||||
@@ -1,24 +1,39 @@
|
||||
import { speedtestTrackerRequestHandler } from "@homarr/request-handler/speedtest-tracker";
|
||||
|
||||
import { createManyIntegrationMiddleware } from "../../middlewares/integration";
|
||||
import { settleIntegrationQueries } from "../../settle-integrations";
|
||||
import { settleIntegrationQueries, toPublicIntegrationError } from "../../settle-integrations";
|
||||
import { createTRPCRouter, publicProcedure } from "../../trpc";
|
||||
|
||||
export const speedtestTrackerRouter = createTRPCRouter({
|
||||
getDashboard: publicProcedure
|
||||
.concat(createManyIntegrationMiddleware("query", "speedtestTracker"))
|
||||
.query(async ({ ctx }) => {
|
||||
return await settleIntegrationQueries(ctx.integrations, async (integration) => {
|
||||
const innerHandler = speedtestTrackerRequestHandler.handler(integration, {});
|
||||
const { data, timestamp } = await innerHandler.getDataAsync();
|
||||
return await settleIntegrationQueries(
|
||||
ctx.integrations,
|
||||
async (integration) => {
|
||||
const innerHandler = speedtestTrackerRequestHandler.handler(integration, {});
|
||||
const { data, timestamp } = await innerHandler.getDataAsync();
|
||||
|
||||
return {
|
||||
integrationId: integration.id,
|
||||
integrationName: integration.name,
|
||||
integrationUrl: integration.url,
|
||||
dashboard: data,
|
||||
updatedAt: timestamp,
|
||||
};
|
||||
});
|
||||
return {
|
||||
integrationId: integration.id,
|
||||
integrationName: integration.name,
|
||||
integrationUrl: integration.url,
|
||||
dashboard: data as typeof data | null,
|
||||
updatedAt: timestamp,
|
||||
error: undefined as string | undefined,
|
||||
};
|
||||
},
|
||||
{
|
||||
fallback: (integration, error) => ({
|
||||
integrationId: integration.id,
|
||||
integrationName: integration.name,
|
||||
integrationUrl: integration.url,
|
||||
dashboard: null,
|
||||
updatedAt: new Date(0),
|
||||
error: toPublicIntegrationError(error),
|
||||
}),
|
||||
throwOnAllFailures: true,
|
||||
},
|
||||
);
|
||||
}),
|
||||
});
|
||||
@@ -1,22 +1,37 @@
|
||||
import { tracearrRequestHandler } from "@homarr/request-handler/tracearr";
|
||||
|
||||
import { createManyIntegrationMiddleware } from "../../middlewares/integration";
|
||||
import { settleIntegrationQueries } from "../../settle-integrations";
|
||||
import { settleIntegrationQueries, toPublicIntegrationError } from "../../settle-integrations";
|
||||
import { createTRPCRouter, publicProcedure } from "../../trpc";
|
||||
|
||||
export const tracearrRouter = createTRPCRouter({
|
||||
getDashboard: publicProcedure.concat(createManyIntegrationMiddleware("query", "tracearr")).query(async ({ ctx }) => {
|
||||
return await settleIntegrationQueries(ctx.integrations, async (integration) => {
|
||||
const innerHandler = tracearrRequestHandler.handler(integration, {});
|
||||
const { data, timestamp } = await innerHandler.getDataAsync();
|
||||
return await settleIntegrationQueries(
|
||||
ctx.integrations,
|
||||
async (integration) => {
|
||||
const innerHandler = tracearrRequestHandler.handler(integration, {});
|
||||
const { data, timestamp } = await innerHandler.getDataAsync();
|
||||
|
||||
return {
|
||||
integrationId: integration.id,
|
||||
integrationName: integration.name,
|
||||
integrationUrl: integration.url,
|
||||
dashboard: data,
|
||||
updatedAt: timestamp,
|
||||
};
|
||||
});
|
||||
return {
|
||||
integrationId: integration.id,
|
||||
integrationName: integration.name,
|
||||
integrationUrl: integration.url,
|
||||
dashboard: data as typeof data | null,
|
||||
updatedAt: timestamp,
|
||||
error: undefined as string | undefined,
|
||||
};
|
||||
},
|
||||
{
|
||||
fallback: (integration, error) => ({
|
||||
integrationId: integration.id,
|
||||
integrationName: integration.name,
|
||||
integrationUrl: integration.url,
|
||||
dashboard: null,
|
||||
updatedAt: new Date(0),
|
||||
error: toPublicIntegrationError(error),
|
||||
}),
|
||||
throwOnAllFailures: true,
|
||||
},
|
||||
);
|
||||
}),
|
||||
});
|
||||
@@ -1,7 +1,7 @@
|
||||
import { traefikRequestHandler } from "@homarr/request-handler/traefik";
|
||||
|
||||
import { createManyIntegrationMiddleware } from "../../middlewares/integration";
|
||||
import { settleIntegrationQueries } from "../../settle-integrations";
|
||||
import { settleIntegrationQueries, toPublicIntegrationError } from "../../settle-integrations";
|
||||
import { createTRPCRouter, publicProcedure } from "../../trpc";
|
||||
|
||||
export const traefikRouter = createTRPCRouter({
|
||||
@@ -15,17 +15,32 @@ export const traefikRouter = createTRPCRouter({
|
||||
})
|
||||
.concat(createManyIntegrationMiddleware("query", "traefik"))
|
||||
.query(async ({ ctx }) => {
|
||||
return await settleIntegrationQueries(ctx.integrations, async (integration) => {
|
||||
const innerHandler = traefikRequestHandler.handler(integration, {});
|
||||
const { data, timestamp } = await innerHandler.getDataAsync();
|
||||
return await settleIntegrationQueries(
|
||||
ctx.integrations,
|
||||
async (integration) => {
|
||||
const innerHandler = traefikRequestHandler.handler(integration, {});
|
||||
const { data, timestamp } = await innerHandler.getDataAsync();
|
||||
|
||||
return {
|
||||
integrationId: integration.id,
|
||||
integrationName: integration.name,
|
||||
integrationUrl: integration.url,
|
||||
dashboard: data,
|
||||
updatedAt: timestamp,
|
||||
};
|
||||
});
|
||||
return {
|
||||
integrationId: integration.id,
|
||||
integrationName: integration.name,
|
||||
integrationUrl: integration.url,
|
||||
dashboard: data as typeof data | null,
|
||||
updatedAt: timestamp,
|
||||
error: undefined as string | undefined,
|
||||
};
|
||||
},
|
||||
{
|
||||
fallback: (integration, error) => ({
|
||||
integrationId: integration.id,
|
||||
integrationName: integration.name,
|
||||
integrationUrl: integration.url,
|
||||
dashboard: null,
|
||||
updatedAt: new Date(0),
|
||||
error: toPublicIntegrationError(error),
|
||||
}),
|
||||
throwOnAllFailures: true,
|
||||
},
|
||||
);
|
||||
}),
|
||||
});
|
||||
@@ -2,24 +2,39 @@ import { getIntegrationKindsByCategory } from "@homarr/definitions";
|
||||
import { upsSummariesRequestHandler } from "@homarr/request-handler/ups";
|
||||
|
||||
import { createManyIntegrationMiddleware } from "../../middlewares/integration";
|
||||
import { settleIntegrationQueries } from "../../settle-integrations";
|
||||
import { settleIntegrationQueries, toPublicIntegrationError } from "../../settle-integrations";
|
||||
import { createTRPCRouter, publicProcedure } from "../../trpc";
|
||||
|
||||
export const upsRouter = createTRPCRouter({
|
||||
getSummaries: publicProcedure
|
||||
.concat(createManyIntegrationMiddleware("query", ...getIntegrationKindsByCategory("ups")))
|
||||
.query(async ({ ctx }) => {
|
||||
return await settleIntegrationQueries(ctx.integrations, async (integration) => {
|
||||
const innerHandler = upsSummariesRequestHandler.handler(integration, {});
|
||||
const { data, timestamp } = await innerHandler.getDataAsync();
|
||||
return await settleIntegrationQueries(
|
||||
ctx.integrations,
|
||||
async (integration) => {
|
||||
const innerHandler = upsSummariesRequestHandler.handler(integration, {});
|
||||
const { data, timestamp } = await innerHandler.getDataAsync();
|
||||
|
||||
return {
|
||||
integrationId: integration.id,
|
||||
integrationName: integration.name,
|
||||
integrationUrl: integration.url,
|
||||
summaries: data,
|
||||
updatedAt: timestamp,
|
||||
};
|
||||
});
|
||||
return {
|
||||
integrationId: integration.id,
|
||||
integrationName: integration.name,
|
||||
integrationUrl: integration.url,
|
||||
summaries: data,
|
||||
updatedAt: timestamp,
|
||||
error: undefined as string | undefined,
|
||||
};
|
||||
},
|
||||
{
|
||||
fallback: (integration, error) => ({
|
||||
integrationId: integration.id,
|
||||
integrationName: integration.name,
|
||||
integrationUrl: integration.url,
|
||||
summaries: [],
|
||||
updatedAt: new Date(0),
|
||||
error: toPublicIntegrationError(error),
|
||||
}),
|
||||
throwOnAllFailures: true,
|
||||
},
|
||||
);
|
||||
}),
|
||||
});
|
||||
@@ -1,24 +1,39 @@
|
||||
import { uptimeKumaRequestHandler } from "@homarr/request-handler/uptime-kuma";
|
||||
|
||||
import { createManyIntegrationMiddleware } from "../../middlewares/integration";
|
||||
import { settleIntegrationQueries } from "../../settle-integrations";
|
||||
import { settleIntegrationQueries, toPublicIntegrationError } from "../../settle-integrations";
|
||||
import { createTRPCRouter, publicProcedure } from "../../trpc";
|
||||
|
||||
export const uptimeKumaRouter = createTRPCRouter({
|
||||
getDashboard: publicProcedure
|
||||
.concat(createManyIntegrationMiddleware("query", "uptimeKuma"))
|
||||
.query(async ({ ctx }) => {
|
||||
return await settleIntegrationQueries(ctx.integrations, async (integration) => {
|
||||
const innerHandler = uptimeKumaRequestHandler.handler(integration, {});
|
||||
const { data, timestamp } = await innerHandler.getDataAsync();
|
||||
return await settleIntegrationQueries(
|
||||
ctx.integrations,
|
||||
async (integration) => {
|
||||
const innerHandler = uptimeKumaRequestHandler.handler(integration, {});
|
||||
const { data, timestamp } = await innerHandler.getDataAsync();
|
||||
|
||||
return {
|
||||
integrationId: integration.id,
|
||||
integrationName: integration.name,
|
||||
integrationUrl: integration.url,
|
||||
dashboard: data,
|
||||
updatedAt: timestamp,
|
||||
};
|
||||
});
|
||||
return {
|
||||
integrationId: integration.id,
|
||||
integrationName: integration.name,
|
||||
integrationUrl: integration.url,
|
||||
dashboard: data as typeof data | null,
|
||||
updatedAt: timestamp,
|
||||
error: undefined as string | undefined,
|
||||
};
|
||||
},
|
||||
{
|
||||
fallback: (integration, error) => ({
|
||||
integrationId: integration.id,
|
||||
integrationName: integration.name,
|
||||
integrationUrl: integration.url,
|
||||
dashboard: null,
|
||||
updatedAt: new Date(0),
|
||||
error: toPublicIntegrationError(error),
|
||||
}),
|
||||
throwOnAllFailures: true,
|
||||
},
|
||||
);
|
||||
}),
|
||||
});
|
||||
Reference in new issue
Block a user