diff --git a/src/app/%5Fcache/route.ts b/src/app/%5Fcache/route.ts index dc3a01864..25bcf60f0 100644 --- a/src/app/%5Fcache/route.ts +++ b/src/app/%5Fcache/route.ts @@ -1,19 +1,29 @@ import { type NextRequest } from 'next/server'; -import { revalidatePath } from 'next/cache'; +import { revalidatePath, revalidateTag } from 'next/cache'; import { redirect } from 'next/navigation'; export const dynamic = 'force-dynamic'; export async function GET(request: NextRequest) { const searchParams = request.nextUrl.searchParams; - const queryparam = searchParams.get('path'); - let path = typeof queryparam === 'string' ? queryparam : '/'; + const tagParam = searchParams.get('tag'); + const pathParam = searchParams.get('path'); + const pathTypeParam = searchParams.get('pathType'); + + // Handle tag-based revalidation if provided + if (tagParam) { + revalidateTag(tagParam); + } + + // Handle path-based revalidation if provided + let path = typeof pathParam === 'string' ? pathParam : '/'; if (!path.startsWith('/')) { path = '/' + path; } - // Invalidate the /posts route in the cache - revalidatePath(path); + if (pathParam) { + revalidatePath(path, pathTypeParam === 'layout' ? 'layout' : 'page'); + } redirect(path); } diff --git a/src/app/(simple-mdx)/[...slug]/page.tsx b/src/app/(simple-mdx)/[...slug]/page.tsx index 7797b49de..f573295ac 100644 --- a/src/app/(simple-mdx)/[...slug]/page.tsx +++ b/src/app/(simple-mdx)/[...slug]/page.tsx @@ -9,6 +9,9 @@ import { MDXProps } from 'mdx/types'; import { Metadata } from 'next'; import { notFound } from 'next/navigation'; +// ISR: Revalidate every 24 hours +export const revalidate = 86400; + export function generateStaticParams() { const allFiles = loadMdxDirectory({ baseDirectory: 'content/simple-mdx-pages', diff --git a/src/app/events/page.tsx b/src/app/events/page.tsx index eef597eb3..4021dcb67 100644 --- a/src/app/events/page.tsx +++ b/src/app/events/page.tsx @@ -9,6 +9,9 @@ import { Google } from '@/svg/calendar/Google'; import { Outlook } from '@/svg/calendar/Outlook'; import { Ics } from '@/svg/calendar/Ics'; +// ISR: Revalidate every 12 hours +export const revalidate = 43200; + export const metadata = createMetaData({ title: 'Virtual Coffee Community Events', description: 'See our upcoming events!', diff --git a/src/app/join/page.tsx b/src/app/join/page.tsx index 54fa2a1e8..842153dfc 100644 --- a/src/app/join/page.tsx +++ b/src/app/join/page.tsx @@ -2,6 +2,9 @@ import DefaultLayout from '@/components/layouts/DefaultLayout'; import { createMetaData } from '@/util/createMetaData.server'; import Link from 'next/link'; +// ISR: Revalidate every 24 hours +export const revalidate = 86400; + export const metadata = createMetaData({ title: 'Join Virtual Coffee', description: `Virtual Coffee is an intimate community that welcomes people at all stages of their tech journey.`, diff --git a/src/app/join/thank-you/page.tsx b/src/app/join/thank-you/page.tsx index b29c2c7df..6d435c69a 100644 --- a/src/app/join/thank-you/page.tsx +++ b/src/app/join/thank-you/page.tsx @@ -1,5 +1,8 @@ import DefaultLayout from '@/components/layouts/DefaultLayout'; +// ISR: Revalidate every 24 hours +export const revalidate = 86400; + export const metadata = { title: 'Membership Form Received!', description: `You're now on the membership waiting list!`, diff --git a/src/app/members/page.tsx b/src/app/members/page.tsx index 9e190cac5..033e59f45 100644 --- a/src/app/members/page.tsx +++ b/src/app/members/page.tsx @@ -10,6 +10,9 @@ import 'leaflet/dist/leaflet.css'; import { Suspense } from 'react'; import { MapLoader } from './map-loader'; +// ISR: Revalidate every 24 hours +export const revalidate = 86400; + export const metadata = createMetaData({ title: 'Virtual Coffee Members', description: 'Meet our amazing members!', diff --git a/src/app/monthlychallenges/page.tsx b/src/app/monthlychallenges/page.tsx index 2b9d02a19..a777d3024 100644 --- a/src/app/monthlychallenges/page.tsx +++ b/src/app/monthlychallenges/page.tsx @@ -3,6 +3,9 @@ import { getTotalPairingSessions } from '@/data/monthlyChallenges/pairing-challe import { createMetaData } from '@/util/createMetaData.server'; import Link from 'next/link'; +// ISR: Revalidate every 24 hours +export const revalidate = 86400; + export const metadata = createMetaData({ title: 'Virtual Coffee Monthly Challenges', description: diff --git a/src/app/page.tsx b/src/app/page.tsx index 3df2425bd..772b3ec15 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -11,6 +11,9 @@ import { getNewsletters } from '@/data/newsletters'; import { getSponsors } from '@/data/sponsors'; import { homePageLinks } from '@/util/homePageLinks'; +// ISR: Revalidate every 12 hours +export const revalidate = 43200; + export default async function Home() { const resources = loadMdxDirectory({ baseDirectory: 'content/resources', diff --git a/src/app/podcast/[slug]/page.tsx b/src/app/podcast/[slug]/page.tsx index c97848656..5dd51e78a 100644 --- a/src/app/podcast/[slug]/page.tsx +++ b/src/app/podcast/[slug]/page.tsx @@ -10,6 +10,9 @@ import { sanitizeCmsData } from '@/util/sanitizeCmsData'; import createCmsImage from '@/util/cmsimage'; import { Metadata } from 'next'; +// ISR: Revalidate every 24 hours +export const revalidate = 86400; + export async function generateStaticParams() { const podcastEpisodes = await getEpisodes({ limit: 99 }); diff --git a/src/app/podcast/page.tsx b/src/app/podcast/page.tsx index 9623291bb..31a46b691 100644 --- a/src/app/podcast/page.tsx +++ b/src/app/podcast/page.tsx @@ -6,6 +6,9 @@ import { getEpisodes } from '@/data/podcast'; import { createMetaData } from '@/util/createMetaData.server'; import createCmsImage from '@/util/cmsimage'; +// ISR: Revalidate every 24 hours +export const revalidate = 86400; + export const metadata = createMetaData({ title: 'Virtual Coffee Podcast', description: diff --git a/src/data/events.ts b/src/data/events.ts index bdf5f4bdf..e251134b8 100644 --- a/src/data/events.ts +++ b/src/data/events.ts @@ -1,5 +1,6 @@ 'use server'; +import { unstable_cache } from 'next/cache'; import { GraphQLClient, gql } from 'graphql-request'; import { DateTime } from 'luxon'; import { sanitizeHtml } from '@/util/sanitizeCmsData'; @@ -75,7 +76,7 @@ function createEventsQuery( `; } -export async function getEvents({ +async function getEventsInternal({ limit, }: { limit: number; @@ -150,3 +151,11 @@ export async function getEvents({ return []; } } + +export const getEvents = async ({ limit }: { limit: number }) => { + return unstable_cache( + () => getEventsInternal({ limit }), + [`events-${limit}`], + { revalidate: 43200, tags: ['events'] } + )(); +}; diff --git a/src/data/members/index.ts b/src/data/members/index.ts index 1b23914a0..66732d698 100644 --- a/src/data/members/index.ts +++ b/src/data/members/index.ts @@ -1,5 +1,6 @@ import type { MemberList } from '@/content/members/types'; import { GraphQLClient, gql } from 'graphql-request'; +import { unstable_cache } from 'next/cache'; import teamsData from '@/content/members/teams'; import mockMemberData from '@/data/mocks/memberData'; import { sanitizeHtml } from '@/util/sanitizeCmsData'; @@ -29,12 +30,18 @@ function nonNullable(value: T): value is NonNullable { return value !== null && value !== undefined; } -export async function getMembers(): Promise { +async function getMembersInternal(): Promise { const userData = await loadUserData(); return userData; } +export const getMembers = unstable_cache( + getMembersInternal, + ['members'], + { revalidate: 86400, tags: ['members'] } +); + async function parseMarkdown(markdown: string) { const [unified, remarkParse, remarkRehype, rehypeSanitize, rehypeStringify] = await Promise.all([ diff --git a/src/data/podcast.ts b/src/data/podcast.ts index 0c44068f4..fad67d35c 100644 --- a/src/data/podcast.ts +++ b/src/data/podcast.ts @@ -1,4 +1,5 @@ import { GraphQLClient, gql } from 'graphql-request'; +import { unstable_cache } from 'next/cache'; export const buzzsproutPodcastId = '1558601' as const; @@ -150,7 +151,7 @@ type PodcastEpisodeResponse = { entries: PodcastEpisode[]; }; -export async function getEpisodes({ +async function getEpisodesInternal({ limit = 5, }: { limit?: number } = {}): Promise { if (!(process.env.CMS_URL && process.env.CMS_TOKEN)) { @@ -182,12 +183,22 @@ export async function getEpisodes({ } } -export async function getEpisode({ +export const getEpisodes = async ({ + limit = 5, +}: { limit?: number } = {}): Promise => { + return unstable_cache( + () => getEpisodesInternal({ limit }), + [`podcast-episodes-${limit}`], + { revalidate: 86400, tags: ['podcast'] }, + )(); +}; + +async function getEpisodeInternal({ slug, queryParams = '', }: { slug: PodcastEpisode['slug']; - queryParams?: any; + queryParams?: string; }): Promise { if (!(process.env.CMS_URL && process.env.CMS_TOKEN)) { const fakeData = await import('./mocks/podcast.server'); @@ -231,6 +242,20 @@ export async function getEpisode({ } } +export const getEpisode = async ({ + slug, + queryParams = '', +}: { + slug: PodcastEpisode['slug']; + queryParams?: string; +}): Promise => { + return unstable_cache( + () => getEpisodeInternal({ slug, queryParams }), + [`podcast-episode-${slug}`], + { revalidate: 86400, tags: ['podcast'] }, + )(); +}; + type TranscriptSegment = { speaker: string; startTime: number; @@ -244,7 +269,7 @@ type TranscriptItem = { }; type Transcript = Array; -export async function getTranscript({ +async function getTranscriptInternal({ id, }: Partial): Promise { try { @@ -291,3 +316,13 @@ export async function getTranscript({ return null; } } + +export const getTranscript = async ({ + id, +}: Partial): Promise => { + return unstable_cache( + () => getTranscriptInternal({ id }), + [`podcast-transcript-${id}`], + { revalidate: 86400, tags: ['podcast'] }, + )(); +}; diff --git a/src/data/sponsors.ts b/src/data/sponsors.ts index 1064eff14..2066427f0 100644 --- a/src/data/sponsors.ts +++ b/src/data/sponsors.ts @@ -1,4 +1,5 @@ import { GraphQLClient, gql } from 'graphql-request'; +import { unstable_cache } from 'next/cache'; import mockData from './mocks/sponsors'; import ImgixClient from '@imgix/js-core'; @@ -115,7 +116,7 @@ const emptySponsorsResponse = { supporters: [], }; -export async function getSponsors() { +async function getSponsorsInternal() { // async function main() { let headers: HeadersInit = { @@ -157,7 +158,7 @@ export async function getSponsors() { (tier) => { const sponsors = response.organization.sponsorshipsAsMaintainer.nodes .filter((sponsor) => { - return sponsor.tier.id === tier.id; + return sponsor.tier?.id === tier.id; }) .map((sponsor) => ({ ...sponsor.sponsorEntity, @@ -192,4 +193,10 @@ export async function getSponsors() { return returnVal; } +export const getSponsors = unstable_cache( + getSponsorsInternal, + ['sponsors'], + { revalidate: 86400, tags: ['sponsors'] }, +); + export type SponsorsResponse = Awaited>;