diff --git a/apps/backend/src/jobs/news/feed-builder.ts b/apps/backend/src/jobs/news/feed-builder.ts index 8801b90c..c17995d8 100644 --- a/apps/backend/src/jobs/news/feed-builder.ts +++ b/apps/backend/src/jobs/news/feed-builder.ts @@ -20,6 +20,7 @@ import { writeMaterializedFeed, writeSubscriptionArticles, } from "../../lib/cache/feed-items"; +import { config } from "../../lib/config"; import { createLogger } from "../../lib/logger"; import { getFeedItems } from "./helper"; import type { NewsFeedItem } from "@dashwise/types/sdk"; @@ -40,7 +41,6 @@ export type NewsFeedRecord = { type BuilderOptions = { userId?: string; feedIds?: string[] }; const logger = createLogger("NewsFeedBuilder"); -const SUBSCRIPTION_RETENTION = 500; function itemTime(item: Record) { const value = item.pubDate; @@ -97,7 +97,7 @@ async function fetchAndCacheSubscription(subscription: NewsSubscription, result: try { const raw = await getFeedItems({ feedUrl, - maxItems: SUBSCRIPTION_RETENTION, + maxItems: config.NEWS_SUBSCRIPTION_RETENTION, feedName: subscription.title || feedUrl, linkReplaceRule: subscription.linkReplaceRule, thumbnailOverwriteUrl: subscription.thumbnailOverwriteUrl, diff --git a/apps/backend/src/jobs/news/helper.ts b/apps/backend/src/jobs/news/helper.ts index 5fa0f232..da5953db 100644 --- a/apps/backend/src/jobs/news/helper.ts +++ b/apps/backend/src/jobs/news/helper.ts @@ -90,6 +90,7 @@ export async function getFeedItems({ } as FeedItem; }) .filter((item: FeedItem) => !isNaN(item.pubDate.getTime())) + .sort((left: FeedItem, right: FeedItem) => right.pubDate.getTime() - left.pubDate.getTime()) .slice(0, maxItems); } diff --git a/apps/backend/src/lib/config.ts b/apps/backend/src/lib/config.ts index 8dd637e6..10dbb914 100644 --- a/apps/backend/src/lib/config.ts +++ b/apps/backend/src/lib/config.ts @@ -16,6 +16,10 @@ const fallbackOutlierValue = normalizedOutlierType === "absolute" ? 500 : 50; const resolvedOutlierValue = Number.isFinite(normalizedOutlierValue) && normalizedOutlierValue > 0 ? normalizedOutlierValue : fallbackOutlierValue; +const normalizedNewsSubscriptionRetention = Number(env.NEWS_SUBSCRIPTION_RETENTION); +const resolvedNewsSubscriptionRetention = Number.isInteger(normalizedNewsSubscriptionRetention) && normalizedNewsSubscriptionRetention > 0 + ? normalizedNewsSubscriptionRetention + : 50; const truthyEnv = (value?: string | null): boolean => { if (!value) return false; @@ -61,6 +65,7 @@ export const config = { MONITORING_RUNNER_SCHEDULE: env.MONITORING_RUNNER_SCHEDULE || "*/1 * * * *", UPDATE_CHECK_SCHEDULE: env.UPDATE_CHECK_SCHEDULE || "0 2 * * *", FEED_BUILDING_SCHEDULE: env.FEED_BUILDING_SCHEDULE || "*/30 * * * *", + NEWS_SUBSCRIPTION_RETENTION: resolvedNewsSubscriptionRetention, NOTIFICATION_FORWARDER_SCHEDULE: env.NOTIFICATION_FORWARDER_SCHEDULE || "* * * * *", DEFAULT_INTEGRATIONS_SCHEDULE: env.DEFAULT_INTEGRATIONS_SCHEDULE || "0 4 * * *", PAGECONFIG_CLEANUP_SCHEDULE: env.PAGECONFIG_CLEANUP_SCHEDULE || "0 5 * * *", diff --git a/docs/Configuration.md b/docs/Configuration.md index 641ee5a6..4e8731a5 100644 --- a/docs/Configuration.md +++ b/docs/Configuration.md @@ -67,6 +67,7 @@ You can use the following environment variables for the all-in-one container; th | MONITORING_RUNNER_SCHEDULE | No | `*/1 * * * *` | How often the monitoring runner executes | | UPDATE_CHECK_SCHEDULE | No | `0 2 * * *` | Schedule for update check job | | FEED_BUILDING_SCHEDULE | No | `*/30 * * * *` | Schedule for news feed building job | +| NEWS_SUBSCRIPTION_RETENTION | No | `50` | Maximum number of latest articles retrieved and cached per news subscription | | NOTIFICATION_FORWARDER_SCHEDULE | No | `* * * * *` | Schedule for notification forwarder job | | DEFAULT_INTEGRATIONS_SCHEDULE | No | `0 4 * * *` | Schedule for default integrations sync | | PAGECONFIG_CLEANUP_SCHEDULE | No | - | Deprecated/no-op. Page-config cleanup runs immediately when an integration is deleted; this variable is read for compatibility but does not schedule work. |