2026-03-02 23:10:31 +03:00
|
|
|
|
import { fetchGraphQL } from './graphql-client.js';
|
|
|
|
|
|
|
|
|
|
|
|
//кэширование
|
|
|
|
|
|
import { cache } from '@lib/cache/manager.js';
|
|
|
|
|
|
import { CACHE_TTL } from '@lib/cache/cache-ttl';
|
|
|
|
|
|
|
2026-03-05 00:23:51 +03:00
|
|
|
|
// lib/api/authors.js
|
|
|
|
|
|
export async function getPostsByCoauthorLogin(coauthorLogin, perLoad = 14, after = null) {
|
|
|
|
|
|
const cacheKey = `coauthor-posts:${coauthorLogin}:${perLoad}:${after || 'first-page'}`;
|
2026-03-02 23:10:31 +03:00
|
|
|
|
|
|
|
|
|
|
return await cache.wrap(
|
|
|
|
|
|
cacheKey,
|
|
|
|
|
|
async () => {
|
2026-03-05 00:23:51 +03:00
|
|
|
|
try {
|
|
|
|
|
|
const baseUrl = import.meta.env.WP_REST_BASE_URL?.replace(/\/$/, '');
|
|
|
|
|
|
|
|
|
|
|
|
// Формируем URL
|
|
|
|
|
|
const url = `${baseUrl}/author/${coauthorLogin}/posts?per_page=${perLoad}${
|
|
|
|
|
|
after ? `&cursor=${encodeURIComponent(after)}` : ''
|
|
|
|
|
|
}`;
|
|
|
|
|
|
|
|
|
|
|
|
const response = await fetch(url);
|
|
|
|
|
|
|
|
|
|
|
|
if (!response.ok) {
|
|
|
|
|
|
if (response.status === 404) {
|
|
|
|
|
|
return { posts: [], pageInfo: { hasNextPage: false, endCursor: null } };
|
2026-03-02 23:10:31 +03:00
|
|
|
|
}
|
2026-03-05 00:23:51 +03:00
|
|
|
|
throw new Error(`HTTP error: ${response.status}`);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const data = await response.json();
|
|
|
|
|
|
|
|
|
|
|
|
// Преобразуем данные для компонента
|
|
|
|
|
|
const posts = (data.data || []).map(post => ({
|
|
|
|
|
|
// ID и ссылки
|
|
|
|
|
|
databaseId: post.id,
|
|
|
|
|
|
uri: post.link, // компонент использует uri
|
2026-03-02 23:10:31 +03:00
|
|
|
|
|
2026-03-05 00:23:51 +03:00
|
|
|
|
// Основные поля
|
|
|
|
|
|
title: post.title,
|
|
|
|
|
|
date: post.date,
|
|
|
|
|
|
|
|
|
|
|
|
// Картинка - приводим к формату GraphQL
|
|
|
|
|
|
featuredImage: post.featured_image?.medium ? {
|
|
|
|
|
|
node: {
|
|
|
|
|
|
sourceUrl: post.featured_image.medium,
|
|
|
|
|
|
altText: post.title
|
2026-03-02 23:10:31 +03:00
|
|
|
|
}
|
2026-03-05 00:23:51 +03:00
|
|
|
|
} : null,
|
|
|
|
|
|
|
|
|
|
|
|
// Категории - приводим к формату nodes
|
|
|
|
|
|
categories: {
|
|
|
|
|
|
nodes: (post.categories || []).map(cat => ({
|
|
|
|
|
|
name: cat.name,
|
|
|
|
|
|
slug: cat.slug,
|
|
|
|
|
|
color: cat.color || null
|
|
|
|
|
|
}))
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
// Соавторы - компонент ожидает массив с name и nickname
|
|
|
|
|
|
coauthors: (post.coauthors || []).map(coauthor => ({
|
|
|
|
|
|
name: coauthor.name,
|
|
|
|
|
|
nickname: coauthor.id, // id используем как nickname для ссылки
|
|
|
|
|
|
node: {
|
|
|
|
|
|
name: coauthor.name,
|
|
|
|
|
|
nickname: coauthor.id
|
2026-03-02 23:10:31 +03:00
|
|
|
|
}
|
2026-03-05 00:23:51 +03:00
|
|
|
|
}))
|
|
|
|
|
|
}));
|
2026-03-02 23:10:31 +03:00
|
|
|
|
|
|
|
|
|
|
return {
|
2026-03-05 00:23:51 +03:00
|
|
|
|
posts,
|
|
|
|
|
|
pageInfo: {
|
|
|
|
|
|
hasNextPage: data.pagination?.has_next || false,
|
|
|
|
|
|
endCursor: data.pagination?.next_cursor || null
|
|
|
|
|
|
}
|
2026-03-02 23:10:31 +03:00
|
|
|
|
};
|
2026-03-05 00:23:51 +03:00
|
|
|
|
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error(`❌ Error fetching author posts:`, error);
|
|
|
|
|
|
return { posts: [], pageInfo: { hasNextPage: false, endCursor: null } };
|
2026-03-02 23:10:31 +03:00
|
|
|
|
}
|
|
|
|
|
|
},
|
2026-03-05 00:23:51 +03:00
|
|
|
|
{ ttl: 3600 }
|
2026-03-02 23:10:31 +03:00
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-05 00:23:51 +03:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2026-03-02 23:10:31 +03:00
|
|
|
|
// все посты автора
|
2026-03-05 00:23:51 +03:00
|
|
|
|
export async function getPostsByCoauthorLoginQL(login, first = 14, after = null) {
|
2026-03-02 23:10:31 +03:00
|
|
|
|
// Создаем уникальный ключ для кэша
|
|
|
|
|
|
const cacheKey = `coauthor-posts-by-login:${login}:${first}:${after || 'first-page'}`;
|
|
|
|
|
|
|
|
|
|
|
|
return await cache.wrap(
|
|
|
|
|
|
cacheKey,
|
|
|
|
|
|
async () => {
|
|
|
|
|
|
const query = `
|
|
|
|
|
|
query GetPostsByCoauthorLogin($first: Int!, $after: String, $login: String!) {
|
|
|
|
|
|
contentNodes(
|
|
|
|
|
|
first: $first
|
|
|
|
|
|
after: $after
|
|
|
|
|
|
where: {
|
|
|
|
|
|
contentTypes: [PROFILE_ARTICLE, ANEW]
|
|
|
|
|
|
coauthorLogin: $login
|
|
|
|
|
|
orderby: { field: DATE, order: DESC }
|
|
|
|
|
|
}
|
|
|
|
|
|
) {
|
|
|
|
|
|
pageInfo {
|
|
|
|
|
|
hasNextPage
|
|
|
|
|
|
endCursor
|
|
|
|
|
|
}
|
|
|
|
|
|
nodes {
|
|
|
|
|
|
__typename
|
|
|
|
|
|
id
|
|
|
|
|
|
databaseId
|
|
|
|
|
|
uri
|
|
|
|
|
|
date
|
|
|
|
|
|
|
|
|
|
|
|
... on NodeWithTitle {
|
|
|
|
|
|
title
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
... on NodeWithFeaturedImage {
|
|
|
|
|
|
featuredImage {
|
|
|
|
|
|
node {
|
|
|
|
|
|
sourceUrl(size: LARGE)
|
|
|
|
|
|
altText
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
# Для ваших кастомных типов
|
|
|
|
|
|
... on ProfileArticle {
|
|
|
|
|
|
categories {
|
|
|
|
|
|
nodes {
|
|
|
|
|
|
name
|
|
|
|
|
|
slug
|
|
|
|
|
|
color
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
... on ANew {
|
|
|
|
|
|
categories {
|
|
|
|
|
|
nodes {
|
|
|
|
|
|
name
|
|
|
|
|
|
slug
|
|
|
|
|
|
color
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
... on NodeWithAuthor {
|
|
|
|
|
|
author {
|
|
|
|
|
|
node {
|
|
|
|
|
|
name
|
|
|
|
|
|
avatar {
|
|
|
|
|
|
url
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
# Coauthors для ProfileArticle
|
|
|
|
|
|
... on ProfileArticle {
|
|
|
|
|
|
coauthors {
|
|
|
|
|
|
id
|
|
|
|
|
|
name
|
|
|
|
|
|
firstName
|
|
|
|
|
|
lastName
|
|
|
|
|
|
url
|
|
|
|
|
|
nicename # Добавляем nicename (login)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
# Coauthors для ANew
|
|
|
|
|
|
... on ANew {
|
|
|
|
|
|
coauthors {
|
|
|
|
|
|
id
|
|
|
|
|
|
name
|
|
|
|
|
|
firstName
|
|
|
|
|
|
lastName
|
|
|
|
|
|
url
|
|
|
|
|
|
nicename # Добавляем nicename (login)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
`;
|
|
|
|
|
|
|
|
|
|
|
|
const data = await fetchGraphQL(query, {
|
|
|
|
|
|
first,
|
|
|
|
|
|
after,
|
|
|
|
|
|
login
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// Обрабатываем посты
|
|
|
|
|
|
const posts = data.contentNodes?.nodes?.map(node => {
|
|
|
|
|
|
// Приводим coauthors к единому формату
|
|
|
|
|
|
if (node.coauthors) {
|
|
|
|
|
|
node.coauthors = node.coauthors.map(author => ({
|
|
|
|
|
|
id: author.id,
|
|
|
|
|
|
name: author.name || '',
|
|
|
|
|
|
firstName: author.firstName || '',
|
|
|
|
|
|
lastName: author.lastName || '',
|
|
|
|
|
|
url: author.url || '',
|
|
|
|
|
|
nicename: author.nicename || '' // Добавляем login
|
|
|
|
|
|
}));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return node;
|
|
|
|
|
|
}) || [];
|
|
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
|
posts,
|
|
|
|
|
|
pageInfo: data.contentNodes?.pageInfo || {
|
|
|
|
|
|
hasNextPage: false,
|
|
|
|
|
|
endCursor: null
|
|
|
|
|
|
},
|
|
|
|
|
|
authorLogin: login
|
|
|
|
|
|
};
|
|
|
|
|
|
},
|
|
|
|
|
|
{ ttl: CACHE_TTL.POSTS }
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export async function getAuthorData(slug) {
|
|
|
|
|
|
const cacheKey = `author-data:slug:${slug}`;
|
|
|
|
|
|
|
|
|
|
|
|
return await cache.wrap(
|
|
|
|
|
|
cacheKey,
|
|
|
|
|
|
async () => {
|
|
|
|
|
|
const baseUrl = import.meta.env.WP_REST_BASE_URL.replace(/\/$/, '');
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
const response = await fetch(`${baseUrl}/author/${slug}`);
|
|
|
|
|
|
|
|
|
|
|
|
if (!response.ok) {
|
|
|
|
|
|
if (response.status === 404) {
|
|
|
|
|
|
return null;
|
|
|
|
|
|
}
|
|
|
|
|
|
throw new Error(`HTTP error: ${response.status}`);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const author = await response.json();
|
|
|
|
|
|
|
|
|
|
|
|
// Форматируем ответ
|
|
|
|
|
|
return {
|
|
|
|
|
|
id: author.slug || author.id,
|
|
|
|
|
|
name: author.name,
|
|
|
|
|
|
firstName: author.firstName,
|
|
|
|
|
|
lastName: author.lastName,
|
|
|
|
|
|
avatar: author.photo || author.avatar,
|
|
|
|
|
|
avatar_sizes: author.photo_sizes || {},
|
|
|
|
|
|
bio: author.description,
|
|
|
|
|
|
social: author.social || {},
|
|
|
|
|
|
url: author.url,
|
|
|
|
|
|
type: author.type,
|
|
|
|
|
|
};
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error(`❌ Failed to fetch author ${slug}:`, error);
|
|
|
|
|
|
return null;
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
{ ttl: CACHE_TTL.AUTHOR }
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|