From 9f4982bdcfe35621bf27f87c1c040981ebe4c1bd Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 2 Jun 2026 22:17:03 +0000 Subject: [PATCH 1/2] fix: relax is_published filter, improve fetch logging, add Substack link MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - is_published === true was silently dropping posts where the field is absent (common on the public API) — changed to !== false so only explicitly unpublished posts are excluded - Replace log() calls with console.error() so fetch errors always appear in CI output regardless of LOG_LEVEL / DEBUG env vars - Add permanent 'Read on Substack' link at the top of the essays section as a reliable fallback if the API fetch returns nothing https://claude.ai/code/session_01P9Bn6gkWtM15yEzGf79XCQ --- lib/get-substack-essays.ts | 29 ++++++++++++++--------------- pages/index.tsx | 9 +++++++++ 2 files changed, 23 insertions(+), 15 deletions(-) diff --git a/lib/get-substack-essays.ts b/lib/get-substack-essays.ts index a726d87..0bb20bf 100644 --- a/lib/get-substack-essays.ts +++ b/lib/get-substack-essays.ts @@ -37,18 +37,23 @@ export async function getSubstackEssays( const response = await fetch(apiUrl, { headers }); if (!response.ok) { - log("ERROR", `Failed to fetch Substack essays from ${substackHandle}`, { - status: response.status, - response: await response.text(), - }); + const body = await response.text(); + console.error(`[substack] fetch failed: ${response.status}`, body.slice(0, 200)); return []; } const data = await response.json(); - // Extract essays from the Substack API response - const essays = (data || []) - .filter((post: any) => post.is_published === true) // Only published posts + if (!Array.isArray(data)) { + console.error(`[substack] unexpected response shape`, JSON.stringify(data).slice(0, 200)); + return []; + } + + // Extract essays from the Substack API response. + // The public endpoint already filters to published posts; avoid strict + // is_published === true which drops posts where the field is absent. + const essays = data + .filter((post: any) => post.is_published !== false) .map((post: any) => ({ id: String(post.id), title: post.title || "", @@ -62,16 +67,10 @@ export async function getSubstackEssays( image: post.cover_image || undefined, })) as SubstackEssay[]; - log("DEBUG", "Fetched Substack essays", { - count: essays.length, - substackHandle, - }); + console.error(`[substack] fetched ${essays.length} essays from ${substackHandle}`); return essays; } catch (err) { - log("ERROR", "Error fetching Substack essays", { - error: err, - substackHandle, - }); + console.error(`[substack] fetch threw`, err); return []; } } diff --git a/pages/index.tsx b/pages/index.tsx index fa9611b..c219a9e 100644 --- a/pages/index.tsx +++ b/pages/index.tsx @@ -115,6 +115,15 @@ export const HomePageContent: React.FC = ({ ESSAYS + + Read on Substack + {(() => { const allItems = [ ...posts From f13c3554402d96609935f8456d2f1860f6aecbed Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 2 Jun 2026 22:17:03 +0000 Subject: [PATCH 2/2] fix: remove build-time Substack fetch (blocked by Cloudflare), fix getSitePosts crash MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Substack's API is behind Cloudflare bot protection which always returns 403 from GitHub Actions — build-time fetching is not viable. Removed the fetch entirely; Substack content is now surfaced via: - "Read on Substack" link at the top of the essays section - Substack embed iframe (loads client-side, not blocked by Cloudflare) Also fixed getSitePosts crash: collection.name[0][0] threw when collection.name was undefined; added optional chaining so it returns undefined and the early-return guard handles it gracefully. https://claude.ai/code/session_01P9Bn6gkWtM15yEzGf79XCQ --- lib/get-site-posts.ts | 2 +- pages/index.tsx | 161 +++++++++++------------------------------- 2 files changed, 41 insertions(+), 122 deletions(-) diff --git a/lib/get-site-posts.ts b/lib/get-site-posts.ts index 2a9511a..2789a1d 100644 --- a/lib/get-site-posts.ts +++ b/lib/get-site-posts.ts @@ -46,7 +46,7 @@ export function getSitePosts(args: { if (!collection) return []; const collectionId = collection.id, - collectionTitle = collection.name[0][0], + collectionTitle = collection.name?.[0]?.[0], collectionSchema = collection.schema; if (!collectionId || collectionTitle !== POSTS_COLLECTION_TITLE) return []; diff --git a/pages/index.tsx b/pages/index.tsx index c219a9e..8cdf281 100644 --- a/pages/index.tsx +++ b/pages/index.tsx @@ -6,7 +6,6 @@ import { getSitePosts } from "lib/get-site-posts"; import * as config from "lib/config"; import { resolveNotionPage } from "lib/resolve-notion-page"; import { resolveArenaChannels } from "lib/resolve-arena-channels"; -import { getSubstackEssays, SubstackEssay } from "lib/get-substack-essays"; import { useDarkMode } from "lib/use-dark-mode"; import { mapPageUrl } from "lib/map-page-url"; import { getLayoutProps } from "lib/get-layout-props"; @@ -34,23 +33,12 @@ export const getStaticProps = async () => { const channels = await resolveArenaChannels(); const siteMap = await getSiteMap(); - // Fetch Substack essays - public API works without a key; key is optional enhancement const substackHandle = process.env.SUBSTACK_HANDLE || "suruleredotdev"; - const substackApiKey = process.env.SUBSTACK_API_KEY; - let substackEssays: SubstackEssay[] = []; - if (substackHandle) { - substackEssays = await getSubstackEssays( - substackHandle, - 10, - substackApiKey - ); - } const props = { ...notionProps, channels, siteMap, - substackEssays, substackHandle, }; @@ -78,7 +66,6 @@ const homePageText = [ const textVersion = 0; interface HomePageContentProps extends types.PageProps { - substackEssays?: SubstackEssay[]; substackHandle?: string; } @@ -88,7 +75,6 @@ export const HomePageContent: React.FC = ({ pageId, channels, siteMap, - substackEssays = [], substackHandle = "suruleredotdev", }) => { // TODO: render from root page block @@ -124,110 +110,46 @@ export const HomePageContent: React.FC = ({ > Read on Substack - {(() => { - const allItems = [ - ...posts - ?.filter((post) => post.public == true) - .map((post) => ({ ...post, isExternal: false, image: undefined as string | undefined })), - ...substackEssays.map((essay) => ({ - ...essay, - isExternal: true, - id: `substack-${essay.id}`, - })), - ].sort((a, b) => b.published - a.published); - - return ( - <> - {allItems.length > 0 && ( - - )} - {substackEssays.length === 0 && ( -