feat(WEB-104): add on-demand revalidation for cache
This commit is contained in:
@@ -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"
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
45
app/api/revalidate/route.ts
Normal file
45
app/api/revalidate/route.ts
Normal 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 })
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -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
27
constants/homeHrefs.ts
Normal 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
2
env/server.ts
vendored
@@ -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,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user