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}
+
+
+
+
+
+
+ ))}
+
+ );
+};
+
+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,