feat(WEB-104): add on-demand revalidation for cache

This commit is contained in:
Simon Emanuelsson
2024-02-21 08:04:03 +01:00
parent d1d20ce555
commit f6f4e6edb4
11 changed files with 104 additions and 23 deletions

View File

@@ -1,10 +1,10 @@
/* eslint-disable @next/next/no-css-tags */ /* eslint-disable @next/next/no-css-tags */
import AdobeScript from "@/components/Current/AdobeScript"
import Footer from "@/components/Current/Footer" import Footer from "@/components/Current/Footer"
import LangPopup from "@/components/Current/LangPopup" import LangPopup from "@/components/Current/LangPopup"
import Script from "next/script" import Script from "next/script"
import SkipToMainContent from "@/components/SkipToMainContent" import SkipToMainContent from "@/components/SkipToMainContent"
import AdobeScript from "../AdobeScript"
import type { Metadata } from "next" import type { Metadata } from "next"
import type { LangParams, LayoutArgs } from "@/types/params" import type { LangParams, LayoutArgs } from "@/types/params"
@@ -21,8 +21,8 @@ export default function RootLayout({
return ( return (
<html lang={params.lang}> <html lang={params.lang}>
<head> <head>
<link rel="stylesheet" href="/Static/css/core.css"/> <link rel="stylesheet" href="/Static/css/core.css" />
<link rel="stylesheet" href="/Static/css/scandic.css"/> <link rel="stylesheet" href="/Static/css/scandic.css" />
<Script <Script
data-cookieconsent="ignore" data-cookieconsent="ignore"
src="/Static/dist/js/cookie-bot.js?1705409331007" src="/Static/dist/js/cookie-bot.js?1705409331007"

View File

@@ -30,13 +30,7 @@ export default async function CurrentPreviewPage({
throw new Error("Not found") throw new Error("Not found")
} }
return ( return <ContentPage data={response.data} />
<ContentPage
data={response.data}
uri={searchParams.uri}
lang={params.lang}
/>
)
} catch (error) { } catch (error) {
// TODO: throw 500 // TODO: throw 500
console.error(error) console.error(error)

View File

@@ -0,0 +1,45 @@
import { env } from "@/env/server"
import { revalidateTag } from 'next/cache'
import type { NextRequest } from 'next/server'
export async function POST(request: NextRequest) {
try {
const secret = request.nextUrl.searchParams.get("secret") ?? ""
const tag = request.nextUrl.searchParams.get("tag") ?? ""
if (secret !== env.REVALIDATE_SECRET) {
return Response.json(
{
message: 'Invalid secret',
now: Date.now(),
revalidated: false,
},
{
status: 401,
}
)
}
if (!tag) {
return Response.json(
{
message: 'Missing tag param',
now: Date.now(),
revalidated: false,
},
{
status: 400,
}
)
}
revalidateTag(tag)
return Response.json({ revalidated: true, now: Date.now() })
} catch (error) {
console.info("Failed to revalidate tag")
console.error(error)
return Response.json({ revalidated: false, now: Date.now() }, { status: 500 })
}
}

View File

@@ -1,5 +1,5 @@
"use client" "use client"
import { useEffect, useRef, useState } from "react" import { useCallback, useEffect, useRef, useState } from "react"
import { languages } from "@/constants/languages" import { languages } from "@/constants/languages"
@@ -18,9 +18,9 @@ export default function Desktop({
setIsOpen(prevIsOpen => !prevIsOpen) setIsOpen(prevIsOpen => !prevIsOpen)
} }
function close() { const close = useCallback(() => {
setIsOpen(false) setIsOpen(false)
} }, [setIsOpen])
useEffect(() => { useEffect(() => {
function handleClickOutside(evt: Event) { function handleClickOutside(evt: Event) {
@@ -67,6 +67,7 @@ export default function Desktop({
<li className={currentLanguage === languages.no ? styles.active : undefined}> <li className={currentLanguage === languages.no ? styles.active : undefined}>
<a className={styles.link} href={urls.no?.url}>{languages.no}</a> <a className={styles.link} href={urls.no?.url}>{languages.no}</a>
</li> </li>
{/* When we have 6 languages in Contenstack, danish url should come from urls.da?.url */}
<li className={currentLanguage === languages.da ? styles.active : undefined}> <li className={currentLanguage === languages.da ? styles.active : undefined}>
<a className={styles.link} href="https://www.scandichotels.dk/">{languages.da}</a> <a className={styles.link} href="https://www.scandichotels.dk/">{languages.da}</a>
</li> </li>

View File

@@ -40,6 +40,7 @@ export default function Mobile({
<li className={`navbar-language-selector__item ${currentLanguage === languages.no ? "is-active" : ""}`}> <li className={`navbar-language-selector__item ${currentLanguage === languages.no ? "is-active" : ""}`}>
<a href={urls.no?.url}>{languages.no}</a> <a href={urls.no?.url}>{languages.no}</a>
</li> </li>
{/* When we have 6 languages in Contenstack, danish url should come from urls.da?.url */}
<li className={`navbar-language-selector__item ${currentLanguage === languages.da ? "is-active" : ""}`}> <li className={`navbar-language-selector__item ${currentLanguage === languages.da ? "is-active" : ""}`}>
<a href="https://www.scandichotels.dk/">{languages.da}</a> <a href="https://www.scandichotels.dk/">{languages.da}</a>
</li> </li>

View File

@@ -95,7 +95,7 @@ export default function MainMenu({
</ul> </ul>
{urls ? ( {urls ? (
<li className="nav-primary__item hidden-medium hidden-large"> <li className={styles.mobileLi}>
<Mobile currentLanguage={currentLanguage} urls={urls} /> <Mobile currentLanguage={currentLanguage} urls={urls} />
</li> </li>
) : null} ) : null}

View File

@@ -1,12 +1,13 @@
.mainMenu { .mainMenu {
background-color: #fff; background-color: #fff;
background-image: none; background-image: none;
box-shadow: 0 0 7px rgba(0, 0, 0, .75);
max-height: 100%;
overflow: visible;
position: fixed; position: fixed;
top: 0; top: 0;
max-height: 100%;
width: 100%; width: 100%;
z-index: 99999; z-index: 99999;
overflow: visible;
} }
.container { .container {
@@ -217,4 +218,8 @@
display: none; display: none;
padding-top: 0px; padding-top: 0px;
} }
.mobileLi {
display: none;
}
} }

View File

@@ -3,6 +3,8 @@ import { batchRequest } from "@/lib/batchRequest"
import { request } from "@/lib/request" import { request } from "@/lib/request"
import { GetHeader } from "@/lib/graphql/Query/Header.graphql" import { GetHeader } from "@/lib/graphql/Query/Header.graphql"
import { GetDaDeEnUrls, GetFiNoSvUrls } from "@/lib/graphql/Query/LanguageSwitcher.graphql" import { GetDaDeEnUrls, GetFiNoSvUrls } from "@/lib/graphql/Query/LanguageSwitcher.graphql"
import { homeHrefs } from "@/constants/homeHrefs"
import { env } from "@/env/server"
import MainMenu from "./MainMenu" import MainMenu from "./MainMenu"
import OfflineBanner from "./OfflineBanner" import OfflineBanner from "./OfflineBanner"
@@ -41,7 +43,7 @@ export default async function Header({ lang, uid }: LangParams & HeaderProps) {
} }
const currentLanguage = languages[lang] const currentLanguage = languages[lang]
const homeHref = `https://www.scandichotels.com/${lang}` const homeHref = homeHrefs[env.NODE_ENV][lang]
const { frontpage_link_text, logoConnection, menu, top_menu } = data.all_header.items[0] const { frontpage_link_text, logoConnection, menu, top_menu } = data.all_header.items[0]
const logo = logoConnection.edges?.[0]?.node const logo = logoConnection.edges?.[0]?.node
const topMenuMobileLinks = top_menu.links.filter(link => link.show_on_mobile) const topMenuMobileLinks = top_menu.links.filter(link => link.show_on_mobile)

27
constants/homeHrefs.ts Normal file
View File

@@ -0,0 +1,27 @@
export const homeHrefs = {
development: {
da: "https://stage.scandichotels.dk",
de: "https://stage.scandichotels.de",
en: "https://stage.scandichotels.com",
fi: "https://stage.scandichotels.fi",
no: "https://stage.scandichotels.no",
sv: "https://stage.scandichotels.se",
},
production: {
da: "https://www.scandichotels.dk",
de: "https://www.scandichotels.de",
en: "https://www.scandichotels.com",
fi: "https://www.scandichotels.fi",
no: "https://www.scandichotels.no",
sv: "https://www.scandichotels.se",
},
test: {
da: "https://test2.scandichotels.dk",
de: "https://test2.scandichotels.de",
en: "https://test2.scandichotels.com",
fi: "https://test2.scandichotels.fi",
no: "https://test2.scandichotels.no",
sv: "https://test2.scandichotels.se",
},
}

2
env/server.ts vendored
View File

@@ -11,6 +11,7 @@ export const env = createEnv({
CMS_PREVIEW_TOKEN: z.string(), CMS_PREVIEW_TOKEN: z.string(),
NODE_ENV: z.enum(["development", "test", "production"]), NODE_ENV: z.enum(["development", "test", "production"]),
PRINT_QUERY: z.boolean().default(false), PRINT_QUERY: z.boolean().default(false),
REVALIDATE_SECRET: z.string(),
}, },
emptyStringAsUndefined: true, emptyStringAsUndefined: true,
runtimeEnv: { runtimeEnv: {
@@ -22,5 +23,6 @@ export const env = createEnv({
CMS_PREVIEW_TOKEN: process.env.CMS_PREVIEW_TOKEN, CMS_PREVIEW_TOKEN: process.env.CMS_PREVIEW_TOKEN,
NODE_ENV: process.env.NODE_ENV, NODE_ENV: process.env.NODE_ENV,
PRINT_QUERY: process.env.PRINT_QUERY, PRINT_QUERY: process.env.PRINT_QUERY,
REVALIDATE_SECRET: process.env.REVALIDATE_SECRET,
}, },
}) })

View File

@@ -6,18 +6,22 @@ import { cache } from "react"
import type { Data } from "@/types/request" import type { Data } from "@/types/request"
import type { DocumentNode } from "graphql" import type { DocumentNode } from "graphql"
const client = new GraphQLClient(env.CMS_URL, {
fetch: cache(async function (url: URL | RequestInfo, params: RequestInit | undefined) {
return fetch(url, params)
}),
})
export async function request<T>( export async function request<T>(
query: string | DocumentNode, query: string | DocumentNode,
variables?: {}, variables?: {},
next?: NextFetchRequestConfig next?: NextFetchRequestConfig
): Promise<Data<T>> { ): Promise<Data<T>> {
try { try {
const client = new GraphQLClient(env.CMS_URL, {
fetch: cache(async function (url: URL | RequestInfo, params: RequestInit | undefined) { if (next) {
return fetch(url, params) client.requestConfig.next = next
}), }
next,
})
if (env.PRINT_QUERY) { if (env.PRINT_QUERY) {
const print = (await import("graphql/language/printer")).print const print = (await import("graphql/language/printer")).print