diff --git a/packages/backend/index.js b/packages/backend/index.js index 3ac69c90..bd627ebb 100644 --- a/packages/backend/index.js +++ b/packages/backend/index.js @@ -14,6 +14,7 @@ const ensRoutes = require("./routes/ens"); const apiRoutes = require("./routes/api"); const cohortRoutes = require("./routes/cohorts"); const notificationsRoutes = require("./routes/notifications"); +const blogRoutes = require("./routes/blog"); const app = express(); @@ -31,6 +32,7 @@ app.use("/ens", ensRoutes); app.use("/api", apiRoutes); app.use("/cohorts", cohortRoutes); app.use("/notifications", notificationsRoutes); +app.use("/blog", blogRoutes); app.get("/sign-message", async (req, res) => { const messageId = req.query.messageId; diff --git a/packages/backend/routes/blog.js b/packages/backend/routes/blog.js new file mode 100644 index 00000000..4fec59e1 --- /dev/null +++ b/packages/backend/routes/blog.js @@ -0,0 +1,40 @@ +const express = require("express"); +const axios = require("axios"); +const xml2js = require("xml2js"); + +const router = express.Router(); + +router.get("/posts", async (req, res) => { + try { + const response = await axios.get("https://buidlguidl.substack.com/feed"); + const xml = response.data; + const parser = new xml2js.Parser(); + + parser.parseString(xml, (err, result) => { + if (err) { + console.error("Failed to parse blog feed:", err); + res.status(500).json({ error: "Failed to parse blog feed" }); + } else { + const posts = result.rss.channel[0].item.map(item => { + const date = new Date(item.pubDate[0]); + const formattedDate = `${date.toLocaleString('default', { month: 'short' }).toUpperCase()} ${date.getDate()}`; + + return { + title: item.title[0], + description: item.description[0], + link: item.link[0], + pubDate: formattedDate, + imageUrl: item.enclosure ? item.enclosure[0].$.url : null + }; + }); + + res.status(200).json(posts.slice(0, 3)); // Return the last 3 posts + } + }); + } catch (error) { + console.error("Error fetching blog posts:", error); + res.status(500).json({ error: "Internal Server Error" }); + } +}); + +module.exports = router; diff --git a/packages/react-app/components/home/BlogSection.jsx b/packages/react-app/components/home/BlogSection.jsx new file mode 100644 index 00000000..d714e7fa --- /dev/null +++ b/packages/react-app/components/home/BlogSection.jsx @@ -0,0 +1,38 @@ +import React from 'react'; +import { Container, VStack, Box, Heading, Text, Image, Link, Flex } from '@chakra-ui/react'; +import useCustomColorModes from "../../hooks/useCustomColorModes"; + +const BlogSection = ({ posts }) => { + const { baseColor, secondaryFontColor } = useCustomColorModes(); + + if (!posts || posts.length === 0) { + return null; + } + + return ( + + + Shipping Log + + + {posts.map((post, index) => ( + + + + + + {post.title} + {post.description} + {post.pubDate} + + {post.title} + + + + + ))} + + ); +}; + +export default BlogSection; diff --git a/packages/react-app/data/api/blog.js b/packages/react-app/data/api/blog.js new file mode 100644 index 00000000..a4f2d0b9 --- /dev/null +++ b/packages/react-app/data/api/blog.js @@ -0,0 +1,12 @@ +import axios from "axios"; +import { SERVER_URL as serverUrl } from "../../constants"; + +export const getRecentPosts = async () => { + try { + const response = await axios.get(`${serverUrl}/blog/posts`); + return response.data; + } catch (error) { + console.error("Error fetching recent posts:", error); + return []; + } +}; diff --git a/packages/react-app/pages/index.jsx b/packages/react-app/pages/index.jsx index e51e162c..eb657a76 100644 --- a/packages/react-app/pages/index.jsx +++ b/packages/react-app/pages/index.jsx @@ -5,8 +5,10 @@ import HeroSection from "../components/home/HeroSection"; import ActivitySection from "../components/home/ActivitySection"; import { getAllBuilds, getAllEvents } from "../data/api"; import RecentBuildsSection from "../components/home/RecentBuildsSection"; +import { getRecentPosts } from "../data/api/blog"; +import BlogSection from "../components/home/BlogSection"; -export default function Index({ bgStats, events, builds }) { +export default function Index({ bgStats, events, builds, posts }) { return ( <> + ); } @@ -25,6 +28,7 @@ export async function getStaticProps() { const stats = await getStats(); const events = await getAllEvents(null, 10); const builds = (await getAllBuilds()).sort((a, b) => b.submittedTimestamp - a.submittedTimestamp).slice(0, 4); + const posts = await getRecentPosts(); return { props: { @@ -38,6 +42,7 @@ export async function getStaticProps() { }, events, builds, + posts, }, // 2 hours caching revalidate: 7200,