Optimize content fetching and update configurations

Content fetching in the marketing section has been refactored to utilize the Next.js cache, which significantly improves performance. The date format of publishedAt has been updated to be more consistent across files. Code related to CSRF token, fonts, and metadata has been refactored into separate files for easier maintenance and readability.
This commit is contained in:
giancarlo
2024-04-19 19:21:54 +08:00
parent c121a3bdad
commit b71b580331
12 changed files with 116 additions and 69 deletions

View File

@@ -36,7 +36,7 @@ export async function generateMetadata({
title,
description,
type: 'article',
publishedTime: publishedAt?.toDateString(),
publishedTime: publishedAt,
url: post.url,
images: image
? [

View File

@@ -20,7 +20,7 @@ export const PostHeader: React.FC<{
<div>
<span className={'text-muted-foreground'}>
<DateFormatter dateString={publishedAt.toISOString()} />
<DateFormatter dateString={publishedAt} />
</span>
</div>

View File

@@ -50,7 +50,7 @@ export function PostPreview({
<div className="flex flex-row items-center space-x-2 text-sm">
<div className="text-muted-foreground">
<DateFormatter dateString={publishedAt.toISOString()} />
<DateFormatter dateString={publishedAt} />
</div>
</div>
</div>

View File

@@ -1,3 +1,5 @@
import { unstable_cache as cache } from 'next/dist/server/web/spec-extension/unstable-cache';
import { createCmsClient } from '@kit/cms';
import { If } from '@kit/ui/if';
import { Trans } from '@kit/ui/trans';
@@ -18,22 +20,33 @@ export const generateMetadata = async () => {
};
};
const getContentItems = cache(
async (language: string | undefined, limit: number, offset: number) => {
const client = await createCmsClient();
return client.getContentItems({
collection: 'posts',
limit,
offset,
language,
sortBy: 'publishedAt',
sortDirection: 'desc',
});
},
);
async function BlogPage({ searchParams }: { searchParams: { page: string } }) {
const { t, resolvedLanguage: language } = await createI18nServerInstance();
const cms = await createCmsClient();
const page = searchParams.page ? parseInt(searchParams.page) : 0;
const limit = 10;
const offset = page * limit;
const { items: posts, total } = await cms.getContentItems({
collection: 'posts',
const { total, items: posts } = await getContentItems(
language,
limit,
offset,
language,
sortBy: 'publishedAt',
sortDirection: 'desc',
});
);
return (
<>

View File

@@ -1,5 +1,4 @@
import { cache } from 'react';
import { unstable_cache as cache } from 'next/cache';
import { notFound } from 'next/navigation';
import { ContentRenderer, createCmsClient } from '@kit/cms';

View File

@@ -1,3 +1,5 @@
import { unstable_cache as cache } from 'next/cache';
import { createCmsClient } from '@kit/cms';
import { PageBody } from '@kit/ui/page';
@@ -14,14 +16,18 @@ export const generateMetadata = async () => {
};
};
async function DocsPage() {
const getContentItems = cache(async (resolvedLanguage: string | undefined) => {
const client = await createCmsClient();
const { t, resolvedLanguage } = await createI18nServerInstance();
const { items } = await client.getContentItems({
return client.getContentItems({
collection: 'documentation',
language: resolvedLanguage,
});
});
async function DocsPage() {
const { t, resolvedLanguage } = await createI18nServerInstance();
const { items } = await getContentItems(resolvedLanguage);
// Filter out any docs that have a parentId, as these are children of other docs
const cards = items.filter((item) => !item.parentId);

View File

@@ -1,32 +1,17 @@
import { Urbanist as HeadingFont, Inter as SansFont } from 'next/font/google';
import Head from 'next/head';
import { cookies, headers } from 'next/headers';
import { cookies } from 'next/headers';
import { Toaster } from '@kit/ui/sonner';
import { cn } from '@kit/ui/utils';
import { CsrfTokenMeta } from '~/components/csrf-token-meta';
import { RootProviders } from '~/components/root-providers';
import appConfig from '~/config/app.config';
import { heading, sans } from '~/lib/fonts';
import { createI18nServerInstance } from '~/lib/i18n/i18n.server';
import { rootMetadata } from '~/lib/root-metdata';
import '../styles/globals.css';
const sans = SansFont({
subsets: ['latin'],
variable: '--font-sans',
fallback: ['system-ui', 'Helvetica Neue', 'Helvetica', 'Arial'],
preload: true,
weight: ['300', '400', '500', '600', '700'],
});
const heading = HeadingFont({
subsets: ['latin'],
variable: '--font-heading',
fallback: ['system-ui', 'Helvetica Neue', 'Helvetica', 'Arial'],
preload: true,
weight: ['500', '700'],
});
export default async function RootLayout({
children,
}: {
@@ -34,9 +19,10 @@ export default async function RootLayout({
}) {
const { language } = await createI18nServerInstance();
const theme = getTheme();
const className = getClassName(theme);
return (
<html lang={language} className={getClassName(theme)}>
<html lang={language} className={className}>
<Head>
<CsrfTokenMeta />
</Head>
@@ -71,33 +57,4 @@ function getTheme() {
return cookies().get('theme')?.value;
}
export const metadata = {
title: appConfig.name,
description: appConfig.description,
metadataBase: new URL(appConfig.url),
openGraph: {
url: appConfig.url,
siteName: appConfig.name,
description: appConfig.description,
},
twitter: {
card: 'summary_large_image',
title: appConfig.title,
description: appConfig.description,
},
icons: {
icon: '/images/favicon/favicon.ico',
shortcut: '/shortcut-icon.png',
apple: '/images/favicon/apple-touch-icon.png',
other: {
rel: 'apple-touch-icon-precomposed',
url: '/apple-touch-icon-precomposed.png',
},
},
};
function CsrfTokenMeta() {
const csrf = headers().get('x-csrf-token') ?? '';
return <meta content={csrf} name="csrf-token" />;
}
export const metadata = rootMetadata;

View File

@@ -0,0 +1,12 @@
import { headers } from 'next/headers';
/**
* @description This component is used to render the CSRF token as a meta tag.
* this tag can be retrieved for use in forms that require CSRF protection.
* @constructor
*/
export function CsrfTokenMeta() {
const csrf = headers().get('x-csrf-token') ?? '';
return <meta content={csrf} name="csrf-token" />;
}

30
apps/web/lib/fonts.ts Normal file
View File

@@ -0,0 +1,30 @@
import { Urbanist as HeadingFont, Inter as SansFont } from 'next/font/google';
/**
* @sans
* @description Define here the sans font.
* By default, it uses the Inter font from Google Fonts.
*/
const sans = SansFont({
subsets: ['latin'],
variable: '--font-sans',
fallback: ['system-ui', 'Helvetica Neue', 'Helvetica', 'Arial'],
preload: true,
weight: ['300', '400', '500', '600', '700'],
});
/**
* @heading
* @description Define here the heading font.
* By default, it uses the Urbanist font from Google Fonts.
*/
const heading = HeadingFont({
subsets: ['latin'],
variable: '--font-heading',
fallback: ['system-ui', 'Helvetica Neue', 'Helvetica', 'Arial'],
preload: true,
weight: ['500', '700'],
});
// we export these fonts into the root layout
export { sans, heading };

View File

@@ -0,0 +1,32 @@
import { Metadata } from 'next';
import appConfig from '~/config/app.config';
/**
* @name rootMetadata
* @description Define the root metadata for the application.
*/
export const rootMetadata: Metadata = {
title: appConfig.name,
description: appConfig.description,
metadataBase: new URL(appConfig.url),
openGraph: {
url: appConfig.url,
siteName: appConfig.name,
description: appConfig.description,
},
twitter: {
card: 'summary_large_image',
title: appConfig.title,
description: appConfig.description,
},
icons: {
icon: '/images/favicon/favicon.ico',
shortcut: '/shortcut-icon.png',
apple: '/images/favicon/apple-touch-icon.png',
other: {
rel: 'apple-touch-icon-precomposed',
url: '/apple-touch-icon-precomposed.png',
},
},
};

View File

@@ -6,7 +6,7 @@ export namespace Cms {
url: string;
description: string | undefined;
content: unknown;
publishedAt: Date;
publishedAt: string;
image: string | undefined;
slug: string;
categories: Category[];

View File

@@ -16,8 +16,6 @@ export class KeystaticClient implements CmsClient {
const docs = await reader.collections[collection].all();
console.log(docs);
const startOffset = options?.offset ?? 0;
const endOffset = startOffset + (options?.limit ?? 10);
@@ -144,7 +142,7 @@ export class KeystaticClient implements CmsClient {
url: item.slug,
slug: item.slug,
description: item.entry.description,
publishedAt,
publishedAt: publishedAt.toISOString(),
content,
image: item.entry.image ?? undefined,
categories: