add files

This commit is contained in:
Andrey Kuvshinov
2025-12-11 01:12:45 +03:00
commit 22358272c6
31 changed files with 7392 additions and 0 deletions

24
src/lib/api/endnews.ts Normal file
View File

@@ -0,0 +1,24 @@
import { fetchGraphQL } from './graphql-client.js';
interface EndnewsPost {
title: string;
uri: string;
date: string;
}
export async function getLatestEndnews(count = 12): Promise<EndnewsPost[]> {
const query = `
query GetEndnews($count: Int!) {
endnews(first: $count, where: {orderby: {field: DATE, order: DESC}}) {
nodes {
title
uri
date
}
}
}
`;
const data = await fetchGraphQL(query, { count });
return data.endnews?.nodes || [];
}

View File

@@ -0,0 +1,42 @@
// src/lib/api/graphql-client.ts
export async function fetchGraphQL<T = any>(
query: string,
variables: Record<string, any> = {}
): Promise<T> {
const endpoint = import.meta.env.WP_GRAPHQL_ENDPOINT;
if (!endpoint) {
throw new Error("WP_GRAPHQL_ENDPOINT is not defined in environment variables");
}
try {
const response = await fetch(endpoint, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ query, variables }),
});
if (!response.ok) {
throw new Error(`HTTP error ${response.status}`);
}
const { data, errors } = await response.json();
if (errors) {
console.error("GraphQL Errors:", errors);
throw new Error(errors[0]?.message || "GraphQL query failed");
}
return data;
} catch (error) {
if (error instanceof Error) {
console.error("GraphQL request failed:", error.message);
throw error;
}
throw new Error("Unknown error in GraphQL request");
}
}

49
src/lib/api/load-posts.ts Normal file
View File

@@ -0,0 +1,49 @@
// src/pages/api/load-posts/index.ts
import type { APIRoute } from 'astro';
export const POST: APIRoute = async ({ request }) => {
try {
const body = await request.json();
const { after = null, first = 8 } = body;
// Динамический импорт
const { getLatestPosts } = await import('../../../../lib/api/posts.js');
const { posts, pageInfo } = await getLatestPosts(first, after);
return new Response(
JSON.stringify({
success: true,
posts,
pageInfo
}),
{
status: 200,
headers: {
'Content-Type': 'application/json',
'Cache-Control': 'public, max-age=60'
}
}
);
} catch (error) {
console.error('Error loading posts:', error);
return new Response(
JSON.stringify({
success: false,
error: 'Failed to load posts',
posts: [],
pageInfo: { hasNextPage: false, endCursor: null }
}),
{
status: 500,
headers: {
'Content-Type': 'application/json'
}
}
);
}
};
export const prerender = false;

View File

@@ -0,0 +1,49 @@
// src/pages/api/load-posts/index.ts
import type { APIRoute } from 'astro';
export const POST: APIRoute = async ({ request }) => {
try {
const body = await request.json();
const { after = null, first = 8 } = body;
// Динамический импорт
const { getLatestPosts } = await import('../../../../lib/api/posts.js');
const { posts, pageInfo } = await getLatestPosts(first, after);
return new Response(
JSON.stringify({
success: true,
posts,
pageInfo
}),
{
status: 200,
headers: {
'Content-Type': 'application/json',
'Cache-Control': 'public, max-age=60'
}
}
);
} catch (error) {
console.error('Error loading posts:', error);
return new Response(
JSON.stringify({
success: false,
error: 'Failed to load posts',
posts: [],
pageInfo: { hasNextPage: false, endCursor: null }
}),
{
status: 500,
headers: {
'Content-Type': 'application/json'
}
}
);
}
};
export const prerender = false;

50
src/lib/api/menu.ts Normal file
View File

@@ -0,0 +1,50 @@
import { fetchGraphQL } from './graphql-client.js';
import type { MenuItem } from '../types/graphql.js';
// Функция ДЛЯ ГЛАВНОГО МЕНЮ (максимум 5 пунктов)
export async function getMainHeaderMenu(): Promise<MenuItem[]> {
const query = `
query GetMainHeaderMenu {
menu(id: "103245", idType: DATABASE_ID) {
menuItems(
first: 5, # Берем только 5
where: {parentId: null} # Только верхний уровень
) {
nodes {
id
label
url
target
order
cssClasses
}
}
}
}
`;
try {
const data = await fetchGraphQL(query);
const items = data.menu?.menuItems?.nodes || [];
// Сортируем по order и гарантируем максимум 5
return items
.sort((a: MenuItem, b: MenuItem) => a.order - b.order)
.slice(0, 5);
} catch (error) {
console.error('Ошибка загрузки главного меню:', error);
// Запасной вариант на случай ошибки (ровно 5 пунктов)
return [
{ id: '1', label: 'Главная', url: '/', order: 0 },
{ id: '2', label: 'Каталог', url: '/catalog', order: 1 },
{ id: '3', label: 'О нас', url: '/about', order: 2 },
{ id: '4', label: 'Контакты', url: '/contacts', order: 3 },
{ id: '5', label: 'Блог', url: '/blog', order: 4 }
];
}
}

99
src/lib/api/posts.ts Normal file
View File

@@ -0,0 +1,99 @@
import { fetchGraphQL } from './graphql-client.js';
import type { ProfileArticle } from '../types/graphql.js';
// lib/api/posts.js
export async function getLatestPosts(first = 12, after = null) {
const query = `
query GetLatestProfileArticles($first: Int!, $after: String) {
profileArticles(
first: $first,
after: $after,
where: {orderby: { field: DATE, order: DESC }}
) {
pageInfo {
hasNextPage
endCursor
}
edges {
cursor
node {
id
databaseId
title
uri
date
featuredImage {
node {
sourceUrl(size: LARGE)
altText
}
}
author {
node {
id
name
firstName
lastName
avatar {
url
}
uri
}
}
categories {
nodes {
id
name
slug
uri
databaseId
}
}
tags {
nodes {
id
name
slug
uri
}
}
}
}
}
}
`;
const data = await fetchGraphQL(query, { first, after });
// Преобразуем edges в nodes
const posts = data.profileArticles?.edges?.map(edge => edge.node) || [];
return {
posts,
pageInfo: data.profileArticles?.pageInfo || { hasNextPage: false, endCursor: null }
};
}
export interface AnewsPost {
title: string;
uri: string;
date: string;
}
export async function getLatestAnews(count = 12): Promise<AnewsPost[]> {
const query = `
query GetAnews($count: Int!) {
aNews(first: $count, where: {orderby: {field: DATE, order: DESC}}) {
nodes {
title
uri
date
}
}
}
`;
const data = await fetchGraphQL(query, { count });
return data.aNews?.nodes || []; // Исправлено: aNews вместо anews
}

22
src/lib/graphql-client.js Normal file
View File

@@ -0,0 +1,22 @@
export async function fetchGraphQL(query, variables = {}) {
const endpoint = import.meta.env.WP_GRAPHQL_ENDPOINT;
if (!endpoint) {
throw new Error("WP_GRAPHQL_ENDPOINT is not defined in .env");
}
const res = await fetch(endpoint, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ query, variables }),
});
const json = await res.json();
if (json.errors) {
console.error("GraphQL error:", json.errors);
throw new Error("GraphQL query failed");
}
return json.data;
}

62
src/lib/types/graphql.ts Normal file
View File

@@ -0,0 +1,62 @@
export interface FeaturedImage {
node: {
sourceUrl: string;
altText: string;
};
}
export interface Author {
node: {
id: string;
name: string;
firstName: string;
lastName: string;
avatar: {
url: string;
};
uri: string;
};
}
export interface Category {
id: string;
name: string;
slug: string;
uri: string;
databaseId: number;
}
export interface Tag {
id: string;
name: string;
slug: string;
uri: string;
}
export interface ProfileArticle {
id: string;
databaseId: number;
title: string;
uri: string;
date: string;
content?: string;
excerpt?: string;
featuredImage: FeaturedImage;
author: Author;
categories: {
nodes: Category[];
};
tags: {
nodes: Tag[];
};
}
// Интерфейс для пункта меню
export interface MenuItem {
id: string;
label: string;
url: string;
target?: string;
order: number;
cssClasses?: string[];
}

48
src/lib/wp-api.js Normal file
View File

@@ -0,0 +1,48 @@
import { fetchGraphQL } from "./graphql-client.js";
export async function getSiteInfo() {
const query = `
query GetSiteInfo {
generalSettings {
title
description
}
}
`;
const data = await fetchGraphQL(query);
return data.generalSettings;
}
export async function getLatestPosts(limit = 10) {
const query = `
query GetLatestProfileArticles($limit: Int!) {
profileArticles(
first: $limit
where: {
orderby: { field: DATE, order: DESC }
}
) {
nodes {
id
databaseId
title
uri
date
featuredImage {
node {
sourceUrl(size: LARGE)
altText
}
}
}
}
}
`;
const data = await fetchGraphQL(query, { limit });
// Используем profileArticles вместо posts
return data.profileArticles.nodes;
}