diff --git a/.env b/.env index 1fa497f3e6..c7c3c50bb4 100644 --- a/.env +++ b/.env @@ -9,6 +9,8 @@ API_BASE_URL=http://prowler-api:8080/api/v1 NEXT_PUBLIC_API_DOCS_URL=http://prowler-api:8080/api/v1/docs AUTH_TRUST_HOST=true UI_PORT=3000 +# Temp URL for feeds need to use actual +RSS_FEED_URL=https://prowler.com/blog/rss # openssl rand -base64 32 AUTH_SECRET="N/c6mnaS5+SWq81+819OrzQZlmx1Vxtp/orjttJSmw8=" diff --git a/ui/actions/feeds/feeds.tsx b/ui/actions/feeds/feeds.tsx new file mode 100644 index 0000000000..76466f9f69 --- /dev/null +++ b/ui/actions/feeds/feeds.tsx @@ -0,0 +1,31 @@ +"use server"; + +import Parser from "rss-parser"; + +const RSS_FEED_URL = process.env.RSS_FEED_URL || ""; + +export const fetchFeeds = async (): Promise => { + if (RSS_FEED_URL?.trim()?.length > 0) { + const parser = new Parser(); + try { + // TODO: Need to update return logic when actual URL is updated for RSS FEED + const feed = await parser.parseURL(RSS_FEED_URL); + return [ + { + title: feed.title, + description: feed.description, + link: feed.link, + lastBuildDate: new Date(feed.lastBuildDate).toLocaleString(), + }, + ]; + } catch (error) { + // eslint-disable-next-line no-console + console.error("Error fetching RSS feed:", error); + return []; + } + } else { + // eslint-disable-next-line no-console + console.warn("RSS url not set"); + return []; + } +}; diff --git a/ui/actions/feeds/index.tsx b/ui/actions/feeds/index.tsx new file mode 100644 index 0000000000..2ab8f864d4 --- /dev/null +++ b/ui/actions/feeds/index.tsx @@ -0,0 +1 @@ +export * from "./feeds"; diff --git a/ui/components/feeds/feeds-client.tsx b/ui/components/feeds/feeds-client.tsx new file mode 100644 index 0000000000..7241c05efe --- /dev/null +++ b/ui/components/feeds/feeds-client.tsx @@ -0,0 +1,87 @@ +"use client"; + +import Link from "next/link"; +import { useState } from "react"; + +import { BellIcon as Icon } from "@/components/icons"; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuTrigger, +} from "@/components/ui/dropdown-menu/dropdown-menu"; +import { cn } from "@/lib/utils"; + +import { Button } from "../ui/button/button"; + +interface Feed { + title: string; + link: string; + description: string; + lastBuildDate: string; +} + +interface FeedsClientProps { + initialFeeds?: Feed[]; +} + +// TODO: Need to update FeedsClientProps with actual interface when actual RSS data finialized +export const FeedsClient: React.FC = ({ + initialFeeds = [], +}) => { + const [feed] = useState(initialFeeds); + + return ( + <> + + + + + +

Feeds

+
+ {feed.length === 0 ? ( +

+ No feeds available +

+ ) : ( + feed.map((item, index) => ( + + +

+ {item.title} +

+

{item.description}

+ + {item.lastBuildDate} + + +
+ )) + )} +
+
+
+ + ); +}; diff --git a/ui/components/feeds/feeds-server.tsx b/ui/components/feeds/feeds-server.tsx new file mode 100644 index 0000000000..78658ad868 --- /dev/null +++ b/ui/components/feeds/feeds-server.tsx @@ -0,0 +1,8 @@ +import { fetchFeeds } from "@/actions/feeds"; +import { FeedsClient } from "@/components/feeds"; + +export const FeedsServer = async () => { + const feeds = await fetchFeeds(); + + return ; +}; diff --git a/ui/components/feeds/index.ts b/ui/components/feeds/index.ts new file mode 100644 index 0000000000..3af78ca16a --- /dev/null +++ b/ui/components/feeds/index.ts @@ -0,0 +1,2 @@ +export * from "./feeds-client"; +export * from "./feeds-server"; diff --git a/ui/components/icons/Icons.tsx b/ui/components/icons/Icons.tsx index 2561fd1cf1..e8f86132dc 100644 --- a/ui/components/icons/Icons.tsx +++ b/ui/components/icons/Icons.tsx @@ -1078,3 +1078,28 @@ export const MutedIcon: React.FC = ({ ); }; + +export const BellIcon: React.FC = ({ + size = 24, + width, + height, + ...props +}) => { + return ( + + + + + ); +}; diff --git a/ui/components/ui/nav-bar/navbar.tsx b/ui/components/ui/nav-bar/navbar.tsx index 5cebc9586f..1ad3c5cfc0 100644 --- a/ui/components/ui/nav-bar/navbar.tsx +++ b/ui/components/ui/nav-bar/navbar.tsx @@ -1,5 +1,6 @@ import { Icon } from "@iconify/react"; +import { FeedsServer } from "@/components/feeds"; import { ThemeSwitch } from "@/components/ThemeSwitch"; import { UserProfileProps } from "@/types"; @@ -26,6 +27,7 @@ export function Navbar({ title, icon, user }: NavbarProps) {

{title}

+ {process.env.NEXT_PUBLIC_IS_CLOUD_ENV === "true" && }
diff --git a/ui/package.json b/ui/package.json index 5d16567d19..eecc0069c8 100644 --- a/ui/package.json +++ b/ui/package.json @@ -35,6 +35,7 @@ "react": "^18.3.1", "react-dom": "^18.3.1", "react-hook-form": "^7.52.2", + "rss-parser": "^3.13.0", "recharts": "^2.15.2", "server-only": "^0.0.1", "shadcn-ui": "^0.2.3", @@ -95,4 +96,4 @@ "@react-types/shared": "3.26.0" }, "version": "0.0.1" -} +} \ No newline at end of file