Update CMS client configuration and refactor content organization

The code changes involve a significant update to the configuration of our CMS client. The nature of retrieving content items has been refactored to be more granular, allowing for the identification and fetching of content from specified collections rather than general categories. These modifications improve the efficiency and specificity of content queries. Furthermore, other changes were made to provide a better alignment of our content structure, including the reorganization of content files and renaming of image paths in various components for consistency.
This commit is contained in:
giancarlo
2024-04-10 15:52:26 +08:00
parent 006c4d430f
commit 44373c0372
39 changed files with 176 additions and 84 deletions

View File

@@ -6,7 +6,6 @@ export namespace Cms {
url: string;
description: string | undefined;
content: unknown;
author: string;
publishedAt: Date;
image: string | undefined;
slug: string;
@@ -30,6 +29,7 @@ export namespace Cms {
}
export interface GetContentItemsOptions {
collection: string;
limit?: number;
offset?: number;
categories?: string[];
@@ -65,10 +65,12 @@ export abstract class CmsClient {
/**
* Retrieves a content item by its ID and type.
* @param id - The ID of the content item.
* @returns A promise that resolves to the content item, or undefined if not found.
*/
abstract getContentItemById(id: string): Promise<Cms.ContentItem | undefined>;
abstract getContentItemBySlug(params: {
slug: string;
collection: string;
}): Promise<Cms.ContentItem | undefined>;
/**
* Retrieves categories based on the provided options.

View File

@@ -9,8 +9,15 @@ const reader = createReader('.', config);
type EntryProps = Entry<(typeof config)['collections']['posts']>;
export class KeystaticClient implements CmsClient {
async getContentItems(options?: Cms.GetContentItemsOptions) {
const docs = await reader.collections.posts.all();
async getContentItems(options: Cms.GetContentItemsOptions) {
const collection =
options.collection as keyof (typeof config)['collections'];
if (!reader.collections[collection]) {
throw new Error(`Collection ${collection} not found`);
}
const docs = await reader.collections[collection].all();
const startOffset = options?.offset ?? 0;
const endOffset = startOffset + (options?.limit ?? 10);
@@ -44,21 +51,26 @@ export class KeystaticClient implements CmsClient {
(item) => item.entry.parent === item.slug,
);
console.log(item);
return this.mapPost(item, children);
}),
);
}
async getContentItemById(id: string) {
const doc = await reader.collections.posts.read(id);
async getContentItemBySlug(params: { slug: string; collection: string }) {
const collection =
params.collection as keyof (typeof config)['collections'];
if (!reader.collections[collection]) {
throw new Error(`Collection ${collection} not found`);
}
const doc = await reader.collections[collection].read(params.slug);
if (!doc) {
return Promise.resolve(undefined);
}
return this.mapPost({ entry: doc, slug: id }, []);
return this.mapPost({ entry: doc, slug: params.slug }, []);
}
async getCategories() {
@@ -96,7 +108,6 @@ export class KeystaticClient implements CmsClient {
slug: item.slug,
description: item.entry.description,
publishedAt,
author: item.entry.author,
content,
image: item.entry.image ?? undefined,
categories:

View File

@@ -27,7 +27,6 @@ function createKeyStaticConfig(path: string) {
tags: fields.array(fields.text({ label: 'Tag' })),
description: fields.text({ label: 'Description' }),
publishedAt: fields.date({ label: 'Published At' }),
author: fields.text({ label: 'Author' }),
parent: fields.relationship({
label: 'Parent',
collection: 'posts',
@@ -52,6 +51,46 @@ function createKeyStaticConfig(path: string) {
}),
},
}),
documentation: collection({
label: 'Documentation',
slugField: 'title',
path: `${path}/documentation/**`,
format: { contentField: 'content' },
schema: {
title: fields.slug({ name: { label: 'Title' } }),
content: fields.document({
label: 'Content',
formatting: true,
dividers: true,
links: true,
images: {
directory: 'public/site/images',
publicPath: '/site/images',
schema: {
title: fields.text({
label: 'Caption',
description:
'The text to display under the image in a caption.',
}),
},
},
}),
image: fields.image({
label: 'Image',
directory: 'public/site/images',
publicPath: '/site/images',
}),
description: fields.text({ label: 'Description' }),
publishedAt: fields.date({ label: 'Published At' }),
order: fields.number({ label: 'Order' }),
parent: fields.relationship({
label: 'Parent',
collection: 'documentation',
}),
categories: fields.array(fields.text({ label: 'Category' })),
tags: fields.array(fields.text({ label: 'Tag' })),
},
}),
},
});
}

View File

@@ -25,7 +25,7 @@ export class WordpressClient implements CmsClient {
*
* @param {Cms.GetContentItemsOptions} options - The options to customize the retrieval of content items.
*/
async getContentItems(options?: Cms.GetContentItemsOptions) {
async getContentItems(options: Cms.GetContentItemsOptions) {
const queryParams = new URLSearchParams({
_embed: 'true',
});
@@ -70,20 +70,21 @@ export class WordpressClient implements CmsClient {
}
const endpoints = [
`/wp-json/wp/v2/pages?${queryParams.toString()}`,
`/wp-json/wp/v2/posts?${queryParams.toString()}`,
`/wp-json/wp/v2/pages?${queryParams.toString()}`,
];
const urls = endpoints.map((endpoint) => `${this.apiUrl}${endpoint}`);
const endpoint =
options.collection === 'posts' ? endpoints[0] : endpoints[1];
const responses = await Promise.all(
urls.map((url) =>
fetch(url).then((value) => value.json() as Promise<WP_REST_API_Post[]>),
),
).then((values) => values.flat().filter(Boolean));
const url = `${this.apiUrl}${endpoint}`;
return await Promise.all(
responses.map(async (item: WP_REST_API_Post) => {
const posts = await fetch(url).then(
(value) => value.json() as Promise<WP_REST_API_Post[]>,
);
return Promise.all(
posts.map(async (item: WP_REST_API_Post) => {
let parentId: string | undefined;
if (!item) {
@@ -94,7 +95,6 @@ export class WordpressClient implements CmsClient {
parentId = item.parent.toString();
}
const author = await this.getAuthor(item.author);
const categories = await this.getCategoriesByIds(item.categories ?? []);
const tags = await this.getTagsByIds(item.tags ?? []);
const image = item.featured_media ? this.getFeaturedMedia(item) : '';
@@ -109,7 +109,6 @@ export class WordpressClient implements CmsClient {
url: item.link,
slug: item.slug,
publishedAt: new Date(item.date),
author: author?.name,
categories: categories,
tags: tags,
parentId,
@@ -120,34 +119,35 @@ export class WordpressClient implements CmsClient {
);
}
async getContentItemById(slug: string) {
async getContentItemBySlug({
slug,
collection,
}: {
slug: string;
collection: string;
}) {
const searchParams = new URLSearchParams({
_embed: 'true',
slug,
});
const endpoints = [
`/wp-json/wp/v2/pages?${searchParams.toString()}`,
`/wp-json/wp/v2/posts?${searchParams.toString()}`,
`/wp-json/wp/v2/pages?${searchParams.toString()}`,
];
const promises = endpoints.map((endpoint) =>
fetch(this.apiUrl + endpoint).then(
(res) => res.json() as Promise<WP_REST_API_Post[]>,
),
const endpoint = collection === 'posts' ? endpoints[0] : endpoints[1];
const responses = await fetch(this.apiUrl + endpoint).then(
(res) => res.json() as Promise<WP_REST_API_Post[]>,
);
const responses = await Promise.all(promises).then((values) =>
values.filter(Boolean),
);
const item = responses[0] ? responses[0][0] : undefined;
const item = responses[0];
if (!item) {
return;
}
const author = await this.getAuthor(item.author);
const categories = await this.getCategoriesByIds(item.categories ?? []);
const tags = await this.getTagsByIds(item.tags ?? []);
const image = item.featured_media ? this.getFeaturedMedia(item) : '';
@@ -163,7 +163,6 @@ export class WordpressClient implements CmsClient {
content: item.content.rendered,
slug: item.slug,
publishedAt: new Date(item.date),
author: author?.name,
categories,
tags,
parentId: item.parent?.toString(),
@@ -313,18 +312,6 @@ export class WordpressClient implements CmsClient {
}));
}
private async getAuthor(id: number) {
const response = await fetch(`${this.apiUrl}/wp-json/wp/v2/users/${id}`);
if (!response.ok) {
return undefined;
}
const data = await response.json();
return { name: data.name };
}
private getFeaturedMedia(post: WP_REST_API_Post) {
const embedded = post._embedded ?? {
'wp:featuredmedia': [],