Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 18 additions & 3 deletions src/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,10 +61,10 @@ const buildSeoSearchParams = (baseParams: URLSearchParams): URLSearchParams => {
"_yoast_wpseo_metadesc,_yoast_wpseo_title,_yoast_wpseo_canonical,_yoast_wpseo_meta-robots-noindex,_yoast_wpseo_meta-robots-nofollow,_yoast_wpseo_opengraph-title,_yoast_wpseo_opengraph-description,_yoast_wpseo_opengraph-image,_yoast_wpseo_twitter-title,_yoast_wpseo_twitter-description,_yoast_wpseo_twitter-image,_rank_math_title,_rank_math_description,_rank_math_canonical_url,_rank_math_robots,_rank_math_facebook_title,_rank_math_facebook_description,_rank_math_facebook_image,_rank_math_twitter_title,_rank_math_twitter_description,_rank_math_twitter_image"
);

// Include additional fields that might contain SEO data
// // Include additional fields that might contain SEO data
baseParams.set(
"_fields",
"id,date,date_gmt,guid,modified,modified_gmt,slug,status,type,link,title,content,excerpt,author,featured_media,comment_status,ping_status,sticky,template,format,meta,categories,tags,class_list,_links,_embedded"
"id,content.rendered,content.raw,block_data,has_blocks,date,date_gmt,guid,modified,modified_gmt,slug,status,type,link,title,excerpt,author,featured_media,comment_status,ping_status,sticky,template,format,meta,categories,tags,class_list,_links,_embedded"
);

return baseParams;
Expand All @@ -73,7 +73,9 @@ const buildSeoSearchParams = (baseParams: URLSearchParams): URLSearchParams => {
export const WordpressServiceLayer = Layer.effect(
WordpressService,
Effect.gen(function* () {
const WORDPRESS_API_URL = yield* Config.url("WORDPRESS_API_URL");
const WORDPRESS_API_URL = yield* Config.url("WORDPRESS_API_URL").pipe(
Effect.map((url) => url.toString().replace(/\/$/, ""))
);
const WORDPRESS_USERNAME = yield* Config.string("WORDPRESS_USERNAME");
const WORDPRESS_PASSWORD = yield* Config.redacted("WORDPRESS_PASSWORD");
const WORDPRESS_STATUS = yield* Config.string("WORDPRESS_STATUS");
Expand Down Expand Up @@ -348,11 +350,24 @@ export const WordpressServiceLayer = Layer.effect(
const url = new URL(
`${WORDPRESS_API_URL}/wp-json/wp/v2/posts?${seoParams.toString()}`
);
yield* Effect.logDebug("Requesting post detail", {
url: url.toString(),
});
const response = yield* httpClient.get(url, {
headers: {
Authorization: `Basic ${Redacted.value(WORDPRESS_API_KEY)}`,
},
});
if (response.status !== 200) {
yield* Effect.logError("Failed to fetch post detail", {
status: response.status,
});
const text = yield* response.text;
yield* Effect.logError(text);
return yield* Effect.fail(
new WordpressError({ message: "Failed to fetch post detail" })
);
}
const json = yield* response.json;
const posts = WpPostDetail.array().safeParse(json);
if (!posts.success) {
Expand Down
208 changes: 201 additions & 7 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,9 +111,13 @@ export const WpPostDetail = z.object({
rendered: z.string(),
}),
content: z.object({
raw: z.string().optional(),
rendered: z.string(),
}),
block_data: z.array(z.unknown()).optional(),
has_blocks: z.boolean().optional(),
excerpt: z.object({
raw: z.string().optional(),
rendered: z.string(),
}),
date: z.string(),
Expand Down Expand Up @@ -203,9 +207,12 @@ export const WpPageDetail = WpPageOverview.extend({
modified: z.string(),
modified_gmt: z.string(),
content: z.object({
raw: z.string().optional(),
rendered: z.string(),
protected: z.boolean(),
}),
has_blocks: z.boolean().optional(),
block_data: z.array(z.unknown()).optional(),
author: z.number(),
featured_media: z.number(),
comment_status: z.string(),
Expand Down Expand Up @@ -249,13 +256,200 @@ export const WpTag = z.object({
seo_meta: WpSeoMeta.optional(),
});

export type WPSeoMeta = z.infer<typeof WpSeoMeta>;
export type WPPostOverview = z.infer<typeof WpPostOverview>;
export type WPPostDetail = z.infer<typeof WpPostDetail>;
export type WPPageOverview = z.infer<typeof WpPageOverview>;
export type WPPageDetail = z.infer<typeof WpPageDetail>;
export type WPCategory = z.infer<typeof WpCategory>;
export type WPTag = z.infer<typeof WpTag>;
export type WPSeoMeta = {
meta_title?: string;
meta_description?: string;
canonical_url?: string;
robots?: string;
og_title?: string;
og_description?: string;
og_image?: string;
og_image_alt?: string;
og_url?: string;
og_type?: string;
og_site_name?: string;
og_locale?: string;
twitter_card?: string;
twitter_title?: string;
twitter_description?: string;
twitter_image?: string;
twitter_image_alt?: string;
twitter_site?: string;
twitter_creator?: string;
schema_type?: string;
schema_json?: string;
focus_keyword?: string;
readability_score?: number;
seo_score?: number;
breadcrumbs?: Array<{
text: string;
url: string;
}>;
};

export type WPPostOverview = {
id: number;
title: {
rendered: string;
};
excerpt: {
rendered: string;
};
date: string;
slug: string;
author?: number;
featured_media?: number;
seo_meta?: WPSeoMeta;
_embedded?: {
author?: Array<{
id: number;
name: string;
url?: string;
description?: string;
avatar_urls?: Record<string, string>;
}>;
"wp:featuredmedia"?: Array<{
id: number;
source_url: string;
alt_text?: string;
media_details?: {
width?: number;
height?: number;
file?: string;
sizes?: Record<string, {
file: string;
width: number;
height: number;
source_url: string;
}>;
};
}>;
};
};

export type WPPostDetail = {
id: number;
title: {
rendered: string;
};
content: {
raw?: string;
rendered: string;
};
block_data?: unknown[];
has_blocks?: boolean;
excerpt: {
raw?: string;
rendered: string;
};
date: string;
modified?: string;
slug: string;
author?: number;
featured_media?: number;
seo_meta?: WPSeoMeta;
reading_time?: number;
word_count?: number;
_embedded?: {
author?: Array<{
id: number;
name: string;
url?: string;
description?: string;
avatar_urls?: Record<string, string>;
}>;
"wp:featuredmedia"?: Array<{
id: number;
source_url: string;
alt_text?: string;
media_details?: {
width?: number;
height?: number;
file?: string;
sizes?: Record<string, {
file: string;
width: number;
height: number;
source_url: string;
}>;
};
}>;
};
};

export type WPPageOverview = {
id: number;
date: string;
slug: string;
status: string;
type: "page";
link: string;
title: {
rendered: string;
};
excerpt: {
rendered: string;
protected: boolean;
};
parent: number;
menu_order: number;
categories: number[];
tags: number[];
class_list: string[];
seo_meta?: WPSeoMeta;
};

export type WPPageDetail = WPPageOverview & {
date_gmt: string;
guid: {
rendered: string;
};
modified: string;
modified_gmt: string;
content: {
raw?: string;
rendered: string;
protected: boolean;
};
has_blocks?: boolean;
block_data?: unknown[];
author: number;
featured_media: number;
comment_status: string;
ping_status: string;
template: string;
meta: {
footnotes: string;
};
seo_meta?: WPSeoMeta;
reading_time?: number;
word_count?: number;
};

export type WPCategory = {
id: number;
count: number;
description: string;
link: string;
name: string;
slug: string;
taxonomy: "category";
parent: number;
meta: unknown[];
seo_meta?: WPSeoMeta;
};

export type WPTag = {
id: number;
count: number;
description: string;
link: string;
name: string;
slug: string;
taxonomy: "post_tag";
meta: unknown[];
seo_meta?: WPSeoMeta;
};

export const WpStatus = z.enum(["draft", "publish"]);
export type WPStatus = z.infer<typeof WpStatus>;
Expand Down
Loading