use proxied fetcher for rss!

This commit is contained in:
Pas 2025-08-03 12:47:08 -06:00
parent a1366a99d0
commit f478b059b3
3 changed files with 117 additions and 133 deletions

View file

@ -8,11 +8,11 @@ import { SettingsView } from "./SettingsView";
import { FancyModal } from "../../Modal";
import { ModalView, NotificationItem, NotificationModalProps } from "../types";
import {
fetchRssFeed,
formatDate,
getAllFeeds,
getCategoryColor,
getCategoryLabel,
getFetchUrl,
getSourceName,
} from "../utils";
@ -111,79 +111,61 @@ export function NotificationModal({ id }: NotificationModalProps) {
if (!feedUrl.trim()) continue;
try {
const fetchUrl = getFetchUrl(feedUrl);
const response = await fetch(fetchUrl);
if (response.ok) {
const responseText = await response.text();
const xmlText = await fetchRssFeed(feedUrl);
// Handle CORS proxy response (JSON wrapper)
let xmlText = responseText;
try {
const jsonResponse = JSON.parse(responseText);
if (jsonResponse.contents) {
xmlText = jsonResponse.contents;
}
} catch {
// If it's not JSON, assume it's direct XML
xmlText = responseText;
}
// Basic validation that we got XML content
if (
xmlText &&
(xmlText.includes("<rss") || xmlText.includes("<feed"))
) {
const parser = new DOMParser();
const xmlDoc = parser.parseFromString(xmlText, "text/xml");
// Basic validation that we got XML content
if (
xmlText &&
(xmlText.includes("<rss") || xmlText.includes("<feed"))
) {
const parser = new DOMParser();
const xmlDoc = parser.parseFromString(xmlText, "text/xml");
// Check for parsing errors
const parserError = xmlDoc.querySelector("parsererror");
if (!parserError && xmlDoc && xmlDoc.documentElement) {
const items = xmlDoc.querySelectorAll("item");
if (items && items.length > 0) {
items.forEach((item) => {
try {
const guid = item.querySelector("guid")?.textContent || "";
const title =
item.querySelector("title")?.textContent || "";
const link = item.querySelector("link")?.textContent || "";
const description =
item.querySelector("description")?.textContent || "";
const pubDate =
item.querySelector("pubDate")?.textContent || "";
const category =
item.querySelector("category")?.textContent || "";
// Check for parsing errors
const parserError = xmlDoc.querySelector("parsererror");
if (!parserError && xmlDoc && xmlDoc.documentElement) {
const items = xmlDoc.querySelectorAll("item");
if (items && items.length > 0) {
items.forEach((item) => {
try {
const guid =
item.querySelector("guid")?.textContent || "";
const title =
item.querySelector("title")?.textContent || "";
const link =
item.querySelector("link")?.textContent || "";
const description =
item.querySelector("description")?.textContent || "";
const pubDate =
item.querySelector("pubDate")?.textContent || "";
const category =
item.querySelector("category")?.textContent || "";
// Skip items without essential data
if (!guid || !title) {
return;
}
// Parse the publication date
const notificationDate = new Date(pubDate);
allNotifications.push({
guid,
title,
link,
description,
pubDate,
category,
source: getSourceName(feedUrl),
});
// Collect GUIDs of notifications older than autoReadDays
if (notificationDate <= autoReadDate) {
autoReadGuids.push(guid);
}
} catch (itemError) {
// Skip malformed items
console.warn("Skipping malformed RSS item:", itemError);
// Skip items without essential data
if (!guid || !title) {
return;
}
});
}
// Parse the publication date
const notificationDate = new Date(pubDate);
allNotifications.push({
guid,
title,
link,
description,
pubDate,
category,
source: getSourceName(feedUrl),
});
// Collect GUIDs of notifications older than autoReadDays
if (notificationDate <= autoReadDate) {
autoReadGuids.push(guid);
}
} catch (itemError) {
// Skip malformed items
console.warn("Skipping malformed RSS item:", itemError);
}
});
}
}
}

View file

@ -3,7 +3,7 @@ import { useEffect, useState } from "react";
import { useOverlayStack } from "@/stores/interface/overlayStack";
import { NotificationItem } from "../types";
import { getAllFeeds, getFetchUrl, getSourceName } from "../utils";
import { fetchRssFeed, getAllFeeds, getSourceName } from "../utils";
// Hook to manage notifications
export function useNotifications() {
@ -25,70 +25,54 @@ export function useNotifications() {
if (!feedUrl.trim()) continue;
try {
const fetchUrl = getFetchUrl(feedUrl);
const response = await fetch(fetchUrl);
if (response.ok) {
const responseText = await response.text();
const xmlText = await fetchRssFeed(feedUrl);
// Handle CORS proxy response (JSON wrapper)
let xmlText = responseText;
try {
const jsonResponse = JSON.parse(responseText);
if (jsonResponse.contents) {
xmlText = jsonResponse.contents;
}
} catch {
// If it's not JSON, assume it's direct XML
xmlText = responseText;
}
// Basic validation that we got XML content
if (
xmlText &&
(xmlText.includes("<rss") || xmlText.includes("<feed"))
) {
const parser = new DOMParser();
const xmlDoc = parser.parseFromString(xmlText, "text/xml");
// Basic validation that we got XML content
if (
xmlText &&
(xmlText.includes("<rss") || xmlText.includes("<feed"))
) {
const parser = new DOMParser();
const xmlDoc = parser.parseFromString(xmlText, "text/xml");
// Check for parsing errors
const parserError = xmlDoc.querySelector("parsererror");
if (!parserError && xmlDoc && xmlDoc.documentElement) {
const items = xmlDoc.querySelectorAll("item");
if (items && items.length > 0) {
items.forEach((item) => {
try {
const guid =
item.querySelector("guid")?.textContent || "";
const title =
item.querySelector("title")?.textContent || "";
const link =
item.querySelector("link")?.textContent || "";
const description =
item.querySelector("description")?.textContent || "";
const pubDate =
item.querySelector("pubDate")?.textContent || "";
const category =
item.querySelector("category")?.textContent || "";
// Check for parsing errors
const parserError = xmlDoc.querySelector("parsererror");
if (!parserError && xmlDoc && xmlDoc.documentElement) {
const items = xmlDoc.querySelectorAll("item");
if (items && items.length > 0) {
items.forEach((item) => {
try {
const guid =
item.querySelector("guid")?.textContent || "";
const title =
item.querySelector("title")?.textContent || "";
const link =
item.querySelector("link")?.textContent || "";
const description =
item.querySelector("description")?.textContent || "";
const pubDate =
item.querySelector("pubDate")?.textContent || "";
const category =
item.querySelector("category")?.textContent || "";
// Skip items without essential data
if (!guid || !title) {
return;
}
allNotifications.push({
guid,
title,
link,
description,
pubDate,
category,
source: getSourceName(feedUrl),
});
} catch (itemError) {
// Skip malformed items silently
// Skip items without essential data
if (!guid || !title) {
return;
}
});
}
allNotifications.push({
guid,
title,
link,
description,
pubDate,
category,
source: getSourceName(feedUrl),
});
} catch (itemError) {
// Skip malformed items silently
}
});
}
}
}

View file

@ -1,5 +1,6 @@
import { proxiedFetch } from "@/backend/helpers/fetch";
const DEFAULT_FEEDS = ["/notifications.xml"];
// const CORS_PROXY = "http://api.allorigins.win/get?url="; // temporarily disabled
export const getAllFeeds = (): string[] => {
try {
@ -18,7 +19,24 @@ export const getFetchUrl = (feedUrl: string): string => {
if (feedUrl.startsWith("/")) {
return feedUrl;
}
return feedUrl; // return `${CORS_PROXY}${encodeURIComponent(feedUrl)}`;
return feedUrl;
};
// New function to fetch RSS feeds using proxiedFetch
export const fetchRssFeed = async (feedUrl: string): Promise<string> => {
if (feedUrl.startsWith("/")) {
// For local feeds, use regular fetch
const response = await fetch(feedUrl);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return response.text();
}
// For external feeds, use proxiedFetch
const response = await proxiedFetch(feedUrl, {
responseType: "text",
});
return response as string;
};
export const getSourceName = (feedUrl: string): string => {