feat: implement webhook to clear apollo cache and periodically revalidate static pages

This commit is contained in:
Hossein Mehrabi 2023-08-31 13:18:36 +03:30
parent 98ec69968c
commit 63261bb140
No known key found for this signature in database
GPG Key ID: 45C04964191AFAA1
10 changed files with 88 additions and 4 deletions

1
.env
View File

@ -1,4 +1,5 @@
UNBODY_API_KEY=
UNBODY_LPE_PROJECT_ID=
SIMPLECAST_ACCESS_TOKEN=
REVALIDATE_WEBHOOK_TOKEN=
NEXT_PUBLIC_SITE_URL=https://press.logos.co

View File

@ -62,6 +62,7 @@ export const getStaticProps: GetStaticProps<PageProps> = async (ctx) => {
props: {
notFound: true,
},
revalidate: 10,
}
}
@ -71,6 +72,7 @@ export const getStaticProps: GetStaticProps<PageProps> = async (ctx) => {
error: 'Something went wrong!',
},
notFound: false,
revalidate: 10,
}
}
@ -81,6 +83,7 @@ export const getStaticProps: GetStaticProps<PageProps> = async (ctx) => {
},
},
notFound: false,
revalidate: 10,
}
}

36
src/pages/api/webhook.ts Normal file
View File

@ -0,0 +1,36 @@
import { readFile, writeFile } from 'fs/promises'
import type { NextApiRequest, NextApiResponse } from 'next'
import path from 'path'
const WEBHOOK_DATA_PATH = path.join(__dirname, '../../webhook_data.json')
const TOKEN = process.env.REVALIDATE_WEBHOOK_TOKEN || ''
export type WebhookData = {
lastUpdate: number
}
const writeWebhookData = async (data: WebhookData) =>
await writeFile(WEBHOOK_DATA_PATH, Buffer.from(JSON.stringify(data)))
export const getWebhookData = async (): Promise<WebhookData> =>
JSON.parse((await readFile(WEBHOOK_DATA_PATH, 'utf-8')) || '{}')
let initialized = false
if (!initialized) writeWebhookData({ lastUpdate: +new Date() })
export default async function handler(
req: NextApiRequest,
res: NextApiResponse<any>,
) {
const {
query: { token = '' },
} = req
if (token !== TOKEN) return res.status(401).json({ message: 'Invalid token' })
writeWebhookData({
lastUpdate: +new Date(),
})
res.status(200).json({})
}

View File

@ -73,6 +73,7 @@ export const getStaticProps = async ({ params }: GetStaticPropsContext) => {
return {
notFound: true,
props: { why: 'no article' },
revalidate: 10,
}
}
@ -98,6 +99,7 @@ export const getStaticProps = async ({ params }: GetStaticPropsContext) => {
},
error: JSON.stringify(errors),
},
revalidate: 10,
}
}

View File

@ -55,6 +55,7 @@ export const getStaticProps: GetStaticProps<PageProps> = async () => {
highlighted,
},
},
revalidate: 10,
}
}

View File

@ -91,6 +91,7 @@ export const getStaticProps = async ({ params }: GetStaticPropsContext) => {
return {
notFound: true,
props: { why: 'no article' },
revalidate: 10,
}
}
@ -99,6 +100,7 @@ export const getStaticProps = async ({ params }: GetStaticPropsContext) => {
episode,
relatedEpisodes,
},
revalidate: 10,
}
}

View File

@ -69,6 +69,7 @@ export const getStaticProps = async ({ params }: GetStaticPropsContext) => {
return {
notFound: true,
props: { why: 'no slug' },
revalidate: 10,
}
}

View File

@ -69,6 +69,7 @@ export const getStaticProps = async ({ params }: GetStaticPropsContext) => {
latestEpisodes: latestEpisodes.map((ep) => ({ ...ep, show: null })),
// errors,
},
revalidate: 10,
}
}

View File

@ -140,5 +140,6 @@ export async function getStaticProps() {
topics,
shows,
},
revalidate: 10,
}
}

View File

@ -1,4 +1,5 @@
import { ApolloClient, InMemoryCache } from '@apollo/client'
import { minutesToMilliseconds } from 'date-fns'
import {
CountDocumentsDocument,
CountDocumentsQueryVariables,
@ -12,6 +13,7 @@ import {
SearchBlocksDocument,
Txt2VecOpenAiGetObjectsTextBlockNearTextInpObj,
} from '../../lib/unbody/unbody.generated'
import { getWebhookData } from '../../pages/api/webhook'
import { ApiResponse, SearchResultItem } from '../../types/data.types'
import { LPE } from '../../types/lpe.types'
import {
@ -52,6 +54,8 @@ export class UnbodyService {
client: ApolloClient<any> = null as any
helpers = UnbodyHelpers
lastUpdate: number = 0
initialDataLastUpdate: number = 0
initialDataPromise: CreatePromiseResult<(typeof this)['initialData']> =
createPromise()
@ -104,9 +108,26 @@ export class UnbodyService {
})
this.loadInitialData(true)
if (process.env.NODE_ENV !== 'development') this.checkForUpdates()
}
private checkForUpdates = async () => {
const data = await getWebhookData()
if (!data) return
if (data.lastUpdate > this.lastUpdate) {
this.lastUpdate = data.lastUpdate
await this.clearCache()
this.loadInitialData(true)
}
setTimeout(this.checkForUpdates, 1000)
}
private _loadInitialData = async () => {
this.initialDataLastUpdate = +new Date()
const articles: LPE.Article.Data[] = await this.fetchAllArticles()
const episodes: LPE.Podcast.Document[] = await this.fetchAllEpisodes()
const staticPages = await this.fetchAllStaticPages()
@ -124,13 +145,26 @@ export class UnbodyService {
}
loadInitialData = async (forced: boolean = false) => {
if (forced) {
if (
forced ||
+new Date() - this.initialDataLastUpdate > minutesToMilliseconds(10)
) {
console.log('load initial data')
this.initialDataPromise &&
this.initialDataPromise.resolve(this.initialData)
this.initialDataPromise = createPromise()
await this.clearCache()
this._loadInitialData()
}
return this.initialDataPromise.promise
}
clearCache = async () => {
this.client.cache.reset()
}
private fetchAllStaticPages = async () => {
const result: LPE.StaticPage.Document[] = []
@ -220,8 +254,8 @@ export class UnbodyService {
data: T | null = null,
errors: any = null,
): ApiResponse<T> => {
if (errors) console.log(errors)
if (errors || !data) {
console.log(errors)
return {
data: data as any,
errors: JSON.stringify(errors),
@ -262,7 +296,8 @@ export class UnbodyService {
getStaticPages = () =>
this.handleRequest<LPE.StaticPage.Document[]>(async () => {
const { staticPages } = await this.loadInitialData()
await this.loadInitialData()
const { staticPages } = this.initialData
return staticPages
}, [])
@ -878,7 +913,8 @@ export class UnbodyService {
skip?: number
}) =>
this.handleRequest(async () => {
const { posts } = await this.loadInitialData()
await this.loadInitialData()
const { posts } = this.initialData
return posts.slice(skip, skip + limit)
}, [])