Files
aragrp/lib/wp-api.ts
T

170 lines
4.3 KiB
TypeScript
Raw Normal View History

2026-02-05 14:54:19 +03:30
const WP_API_BASE = "https://wpa.feep.ir/wp-json/wp/v2";
const FETCH_TIMEOUT_MS = 15_000;
export interface WpMediaSize {
source_url: string;
width?: number;
height?: number;
}
export interface WpFeaturedMedia {
source_url: string;
alt_text?: string;
media_details?: {
width?: number;
height?: number;
sizes?: Record<string, WpMediaSize>;
};
}
export interface WpAuthor {
id: number;
name: string;
slug?: string;
}
export interface WpPost {
id: number;
date: string;
modified?: string;
slug: string;
title: { rendered: string };
content: { rendered: string };
excerpt: { rendered: string };
featured_media: number;
categories: number[];
author?: number;
_embedded?: {
"wp:featuredmedia"?: WpFeaturedMedia[];
author?: WpAuthor[];
};
}
/** Returns thumbnail URL for list/card (medium) or full for single post. */
export function getFeaturedImageUrl(
post: WpPost,
size: "medium" | "medium_large" | "full" = "medium"
): string | null {
const media = post._embedded?.["wp:featuredmedia"]?.[0];
if (!media?.source_url) return null;
const sizes = media.media_details?.sizes;
if (sizes?.[size]?.source_url) return sizes[size].source_url;
if (size !== "full" && sizes?.medium?.source_url) return sizes.medium.source_url;
return media.source_url;
}
export interface WpCategory {
id: number;
name: string;
slug: string;
count: number;
}
export interface GetPostsResult {
posts: WpPost[];
total: number;
totalPages: number;
}
export interface GetPostsOptions {
page?: number;
perPage?: number;
search?: string;
categoryId?: number;
}
async function fetchWithTimeout(
url: string,
options?: RequestInit
): Promise<Response> {
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
try {
const res = await fetch(url, {
...options,
signal: controller.signal,
next: { revalidate: 0 },
});
clearTimeout(timeout);
return res;
} catch (e) {
clearTimeout(timeout);
throw e;
}
}
export async function getPosts(
options: GetPostsOptions = {}
): Promise<GetPostsResult> {
const { page = 1, perPage = 9, search, categoryId } = options;
const params = new URLSearchParams();
params.set("page", String(page));
params.set("per_page", String(perPage));
if (search?.trim()) params.set("search", search.trim());
if (categoryId) params.set("categories", String(categoryId));
params.set("_embed", "1");
const url = `${WP_API_BASE}/posts?${params.toString()}`;
const res = await fetchWithTimeout(url);
if (!res.ok) {
throw new Error(`WP API error: ${res.status} ${res.statusText}`);
}
const total = parseInt(res.headers.get("x-wp-total") ?? "0", 10);
const totalPages = parseInt(res.headers.get("x-wp-totalpages") ?? "1", 10);
const posts: WpPost[] = await res.json();
return { posts, total, totalPages };
}
export async function getPostBySlug(slug: string): Promise<WpPost | null> {
const decodedSlug = decodeURIComponent(slug);
const params = new URLSearchParams({ slug: decodedSlug, _embed: "1" });
const url = `${WP_API_BASE}/posts?${params.toString()}`;
const res = await fetchWithTimeout(url);
if (!res.ok) {
throw new Error(`WP API error: ${res.status} ${res.statusText}`);
}
const posts: WpPost[] = await res.json();
return posts.length > 0 ? posts[0] : null;
}
export async function getRelatedPosts(
postId: number,
categoryIds: number[],
limit = 3
): Promise<WpPost[]> {
const params = new URLSearchParams();
params.set("exclude", String(postId));
params.set("per_page", String(limit));
params.set("_embed", "1");
if (categoryIds.length > 0) {
params.set("categories", String(categoryIds[0]));
}
const url = `${WP_API_BASE}/posts?${params.toString()}`;
const res = await fetchWithTimeout(url);
if (!res.ok) {
throw new Error(`WP API error: ${res.status} ${res.statusText}`);
}
const posts: WpPost[] = await res.json();
return posts;
}
2026-02-05 14:54:19 +03:30
export async function getCategories(): Promise<WpCategory[]> {
const url = `${WP_API_BASE}/categories?per_page=100`;
const res = await fetchWithTimeout(url);
if (!res.ok) {
throw new Error(`WP API error: ${res.status} ${res.statusText}`);
}
const data: WpCategory[] = await res.json();
return data.filter((c) => c.count > 0);
}