fix: resilient RSS/releases fetching and effect deps

- RSS feed: Promise.allSettled so one bad URL doesn't fail all feeds
- Releases: Promise.allSettled so one bad repo doesn't blank widget
- system-resources: add data to useEffect deps
- media-requests: invalidate all mediaRequests queries on answer
This commit is contained in:
Thomas Camlong committed 2026-06-25 14:47:58 +02:00
1 parent 92920db07d
commit 8f4f543d29
4 files changed
+22 -6

No files matched your search

+10 -1
View File
@@ -1,12 +1,15 @@
import { escapeForRegEx } from "@tiptap/react";
import { z } from "zod/v4";
import { createLogger } from "@homarr/core/infrastructure/logs";
import { getIntegrationKindsByCategory } from "@homarr/definitions";
import { releasesRequestHandler } from "@homarr/request-handler/releases";
import { createOneIntegrationMiddleware } from "../../middlewares/integration";
import { createTRPCRouter, publicProcedure } from "../../trpc";
const logger = createLogger({ module: "releases" });
const formatVersionFilterRegex = (versionFilter: z.infer<typeof releaseVersionFilterSchema> | undefined) => {
if (!versionFilter) return undefined;
@@ -38,7 +41,7 @@ export const releasesRouter = createTRPCRouter({
}),
)
.query(async ({ ctx, input }) => {
return await Promise.all(
const settled = await Promise.allSettled(
input.repositories.map(async (repository) => {
const response = await releasesRequestHandler
.handler(ctx.integration, {
@@ -56,5 +59,11 @@ export const releasesRouter = createTRPCRouter({
};
}),
);
return settled.flatMap((result, index) => {
if (result.status === "fulfilled") return [result.value];
logger.warn("Release fetch failed", { repository: input.repositories[index]?.identifier, error: result.reason });
return [];
});
}),
});
+10 -3
View File
@@ -5,11 +5,14 @@ import type { Session } from "@homarr/auth";
import type { Database } from "@homarr/db";
import { eq } from "@homarr/db";
import { items, users } from "@homarr/db/schema";
import { createLogger } from "@homarr/core/infrastructure/logs";
import { rssFeedsRequestHandler } from "@homarr/request-handler/rss-feeds";
import type { WidgetComponentProps } from "../../../../widgets/src";
import { createTRPCRouter, publicProcedure } from "../../trpc";
const logger = createLogger({ module: "rssFeed" });
const feedsInput = z.object({
urls: z.array(z.string()).max(100),
maximumAmountPosts: z.number(),
@@ -21,7 +24,7 @@ export const rssFeedRouter = createTRPCRouter({
? input.urls
: await restrictUrlsAsync(ctx.db, input.urls);
const rssFeeds = await Promise.all(
const settled = await Promise.allSettled(
urls.map(async (url) => {
const innerHandler = rssFeedsRequestHandler.handler({
url,
@@ -31,8 +34,12 @@ export const rssFeedRouter = createTRPCRouter({
}),
);
return rssFeeds
.flatMap((rssFeed) => rssFeed.data.entries)
return settled
.flatMap((result, index) => {
if (result.status === "fulfilled") return result.value.data.entries;
logger.warn("RSS feed fetch failed", { url: urls[index], error: result.reason });
return [];
})
.slice(0, input.maximumAmountPosts)
.toSorted((entryA, entryB) => {
return entryA.published && entryB.published
@@ -200,7 +200,7 @@ interface DecisionButtonsProps {
const DecisionButtons = ({ requestId, integrationId }: DecisionButtonsProps) => {
const utils = clientApi.useUtils();
const { mutate: mutateRequestAnswer } = clientApi.widget.mediaRequests.answerRequest.useMutation({
onSettled: () => void utils.widget.mediaRequests.getLatestRequests.invalidate(),
onSettled: () => void utils.widget.mediaRequests.invalidate(),
});
const t = useScopedI18n("widget.mediaRequests-requestList");
const handleDecision = (answer: RouterInputs["widget"]["mediaRequests"]["answerRequest"]["answer"]) => {
@@ -50,7 +50,7 @@ export default function SystemResources({ integrationIds, options }: WidgetCompo
const firstItem = data[0];
if (!firstItem) return;
setItems((prev) => [...prev, toChartItem(firstItem.healthInfo)].slice(-MAX_QUEUE_SIZE));
}, [dataUpdatedAt]);
}, [dataUpdatedAt, data]);
const showNetwork =
items.length === 0 || (items.every((item) => item.network !== null) && options.visibleCharts.includes("network"));