import { z } from "zod/v4"; import { getIntegrationKindsByCategory } from "@homarr/definitions"; import { releasesRequestHandler } from "@homarr/request-handler/releases"; import { createOneIntegrationMiddleware } from "../../middlewares/integration"; import { createTRPCRouter, publicProcedure } from "../../trpc"; const formatVersionFilterRegex = (versionFilter: z.infer | undefined) => { if (!versionFilter) return undefined; const escapedPrefix = versionFilter.prefix ? RegExp.escape(versionFilter.prefix) : ""; const precision = "[0-9]+\\.".repeat(versionFilter.precision).slice(0, -2); const escapedSuffix = versionFilter.suffix ? RegExp.escape(versionFilter.suffix) : ""; return `^${escapedPrefix}${precision}${escapedSuffix}$`; }; const releaseVersionFilterSchema = z.object({ prefix: z.string().optional(), precision: z.number(), suffix: z.string().optional(), }); export const releasesRouter = createTRPCRouter({ getLatest: publicProcedure .concat(createOneIntegrationMiddleware("query", ...getIntegrationKindsByCategory("releasesProvider"))) .input( z.object({ repositories: z.array( z.object({ id: z.string(), identifier: z.string(), versionFilter: releaseVersionFilterSchema.optional(), }), ), }), ) .query(async ({ ctx, input }) => { return await Promise.all( input.repositories.map(async (repository) => { const innerHandler = releasesRequestHandler.handler(ctx.integration, { id: repository.id, identifier: repository.identifier, versionRegex: formatVersionFilterRegex(repository.versionFilter), }); return await innerHandler.getCachedOrUpdatedDataAsync({ forceUpdate: false, }); }), ); }), });