исправленный пррендринг
This commit is contained in:
1 parent
21936f0aef
commit
8889c6bee7
1 file changed
+143
-66
+143
-66
@@ -1,20 +1,27 @@
|
|||||||
import { readFileSync, writeFileSync, mkdirSync, existsSync } from "fs";
|
import { readFileSync, writeFileSync, mkdirSync, existsSync } from "fs";
|
||||||
import { resolve, dirname } from "path";
|
import { resolve, dirname } from "path";
|
||||||
import { fileURLToPath } from "url";
|
import { fileURLToPath } from "url";
|
||||||
import { client } from "../src/api/client";
|
|
||||||
|
|
||||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||||
|
|
||||||
const products = async function getProducts() {
|
async function getProducts() {
|
||||||
try {
|
try {
|
||||||
console.log("📡 Загрузка товаров из API...");
|
const response = await fetch('https://pvh.projects.rustprogersteam.ru/api/productList');
|
||||||
const response = await client.get("/productList/");
|
const data = await response.json();
|
||||||
return response.data;
|
|
||||||
|
if (Array.isArray(data)) {
|
||||||
|
console.log(`✅ Загружено ${data.length} товаров`);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
return [];
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("❌ Ошибка загрузки из API:", error.message);
|
console.error('Ошибка:', error.message);
|
||||||
console.log("📦 Использую тестовые данные...");
|
return [];
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
|
// Получаем товары
|
||||||
|
const products = await getProducts();
|
||||||
|
|
||||||
const distPath = resolve(__dirname, "../dist");
|
const distPath = resolve(__dirname, "../dist");
|
||||||
const indexHtmlPath = resolve(distPath, "index.html");
|
const indexHtmlPath = resolve(distPath, "index.html");
|
||||||
@@ -27,65 +34,149 @@ if (!existsSync(indexHtmlPath)) {
|
|||||||
// Читаем исходный HTML
|
// Читаем исходный HTML
|
||||||
let baseHtml = readFileSync(indexHtmlPath, "utf-8");
|
let baseHtml = readFileSync(indexHtmlPath, "utf-8");
|
||||||
|
|
||||||
// Добавляем базовые мета-теги для каждой страницы
|
// ============================================
|
||||||
const pages = [
|
// ФУНКЦИИ ДЛЯ ГЕНЕРАЦИИ КОНТЕНТА
|
||||||
|
// ============================================
|
||||||
|
|
||||||
|
function escapeHtml(text) {
|
||||||
|
if (!text) return '';
|
||||||
|
return String(text)
|
||||||
|
.replace(/&/g, '&')
|
||||||
|
.replace(/</g, '<')
|
||||||
|
.replace(/>/g, '>')
|
||||||
|
.replace(/"/g, '"')
|
||||||
|
.replace(/'/g, ''');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Генерация страницы товара
|
||||||
|
function generateProductPage(product) {
|
||||||
|
return `
|
||||||
|
<div class="product-page">
|
||||||
|
<h1>${escapeHtml(product.full_name)}</h1>
|
||||||
|
<div class="product-description">
|
||||||
|
<p>${escapeHtml(product.description || 'Описание отсутствует')}</p>
|
||||||
|
</div>
|
||||||
|
<div class="product-details">
|
||||||
|
<p><strong>Артикул:</strong> ${product.id}</p>
|
||||||
|
</div>
|
||||||
|
</div>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Генерация главной страницы
|
||||||
|
function generateHomePage(products) {
|
||||||
|
const topProducts = products.slice(0, 6).map(product => `
|
||||||
|
<div class="product-card">
|
||||||
|
<h3>${escapeHtml(product.full_name || `Товар ${product.id}`)}</h3>
|
||||||
|
<a href="/product/${product.id}">Подробнее</a>
|
||||||
|
</div>`).join('');
|
||||||
|
|
||||||
|
return `
|
||||||
|
<div class="home-page">
|
||||||
|
<h1>Добро пожаловать в магазин труб!</h1>
|
||||||
|
<p>Широкий выбор канализационных труб, фитингов и комплектующих.</p>
|
||||||
|
<h2>Популярные товары</h2>
|
||||||
|
<div class="products-grid">
|
||||||
|
${topProducts}
|
||||||
|
</div>
|
||||||
|
<a href="/products" class="catalog-link">Перейти в каталог</a>
|
||||||
|
</div>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Генерация страницы каталога
|
||||||
|
function generateCatalogPage(products) {
|
||||||
|
const allProducts = products.map(product => `
|
||||||
|
<div class="product-card">
|
||||||
|
<h2>${escapeHtml(product.full_name || `Товар ${product.id}`)}</h2>
|
||||||
|
<p>${escapeHtml((product.description || '').slice(0, 150))}...</p>
|
||||||
|
<a href="/product/${product.id}">Подробнее</a>
|
||||||
|
</div>`).join('');
|
||||||
|
|
||||||
|
return `
|
||||||
|
<div class="catalog-page">
|
||||||
|
<h1>Каталог товаров</h1>
|
||||||
|
<div class="product-list">
|
||||||
|
${allProducts}
|
||||||
|
</div>
|
||||||
|
</div>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Генерация страницы "О нас"
|
||||||
|
function generateAboutPage() {
|
||||||
|
return `
|
||||||
|
<div class="about-page">
|
||||||
|
<h1>О магазине</h1>
|
||||||
|
<p>Мы занимаемся продажей качественных труб и фитингов для канализации.</p>
|
||||||
|
<h2>Наши преимущества:</h2>
|
||||||
|
<ul>
|
||||||
|
<li>Низкие цены</li>
|
||||||
|
<li>Быстрая доставка</li>
|
||||||
|
<li>Гарантия качества</li>
|
||||||
|
</ul>
|
||||||
|
</div>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const routes = [
|
||||||
{
|
{
|
||||||
path: "/",
|
path: "/",
|
||||||
title: "Магазин труб - Главная",
|
title: "Магазин труб - Главная",
|
||||||
description:
|
description: "Купить канализационные трубы, фитинги и комплектующие по низким ценам.",
|
||||||
"Купить канализационные трубы, фитинги и комплектующие по низким ценам.",
|
getContent: () => generateHomePage(products)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/products",
|
path: "/products",
|
||||||
title: "Каталог труб и фитингов для канализации",
|
title: "Каталог труб и фитингов для канализации",
|
||||||
description:
|
description: "Широкий выбор канализационных труб ПВХ, фитингов, тройников, переходов, колен.",
|
||||||
"Широкий выбор канализационных труб ПВХ, фитингов, тройников, переходов, колен. Высокое качество, низкие цены.",
|
getContent: () => generateCatalogPage(products)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/about",
|
path: "/about",
|
||||||
title: "О магазине труб - Доставка и гарантия",
|
title: "О магазине труб - Доставка и гарантия",
|
||||||
description:
|
description: "Информация о нашей компании. Как мы работаем, условия доставки.",
|
||||||
"Информация о нашей компании. Как мы работаем, условия доставки, гарантия на все товары.",
|
getContent: () => generateAboutPage()
|
||||||
},
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
const allRoutes = [...pages];
|
// Добавляем страницы товаров
|
||||||
|
|
||||||
// Добавляем маршруты товаров
|
|
||||||
for (const product of products) {
|
for (const product of products) {
|
||||||
allRoutes.push({
|
routes.push({
|
||||||
path: `/product/${product.id}`,
|
path: `/product/${product.id}`,
|
||||||
description: product.description,
|
|
||||||
title: product.full_name,
|
title: product.full_name,
|
||||||
|
description: (product.description || ''),
|
||||||
|
getContent: () => generateProductPage(product)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log(`\n📄 Всего страниц для генерации: ${routes.length}\n`);
|
||||||
|
|
||||||
let successCount = 0;
|
let successCount = 0;
|
||||||
let errorCount = 0;
|
|
||||||
|
|
||||||
for (const route of allRoutes) {
|
for (const route of routes) {
|
||||||
try {
|
try {
|
||||||
// Заменяем мета-теги
|
// Генерируем контент для этой страницы
|
||||||
let html = baseHtml
|
const pageContent = route.getContent();
|
||||||
.replace(
|
|
||||||
/<title>.*?<\/title>/,
|
// Вставляем контент в #root
|
||||||
`<title>${escapeHtml(route.title)}</title>`,
|
let html = baseHtml;
|
||||||
)
|
|
||||||
.replace(
|
// Меняем title
|
||||||
/<meta name="description".*?>/,
|
html = html.replace(/<title>.*?<\/title>/, `<title>${escapeHtml(route.title)}</title>`);
|
||||||
`<meta name="description" content="${escapeHtml(route.description)}">`,
|
|
||||||
);
|
// Меняем meta description
|
||||||
|
html = html.replace(/<meta name="description".*?>/, `<meta name="description" content="${escapeHtml(route.description)}">`);
|
||||||
// Добавляем Open Graph теги (для соцсетей)
|
|
||||||
|
// Вставляем контент в #root (ВАЖНО!)
|
||||||
|
html = html.replace('<div id="root"></div>', `<div id="root">${pageContent}</div>`);
|
||||||
|
|
||||||
|
// Добавляем Open Graph теги
|
||||||
html = html.replace(
|
html = html.replace(
|
||||||
"</head>",
|
"</head>",
|
||||||
` <meta property="og:title" content="${escapeHtml(route.title)}">
|
` <meta property="og:title" content="${escapeHtml(route.title)}">
|
||||||
<meta property="og:description" content="${escapeHtml(route.description)}">
|
<meta property="og:description" content="${escapeHtml(route.description)}">
|
||||||
<meta property="og:type" content="website">
|
<meta property="og:type" content="website">
|
||||||
<meta name="robots" content="index, follow">
|
<meta name="robots" content="index, follow">
|
||||||
</head>`,
|
</head>`
|
||||||
);
|
);
|
||||||
|
|
||||||
// Определяем путь для сохранения
|
// Определяем путь для сохранения
|
||||||
let outputPath;
|
let outputPath;
|
||||||
if (route.path === "/") {
|
if (route.path === "/") {
|
||||||
@@ -97,40 +188,21 @@ for (const route of allRoutes) {
|
|||||||
}
|
}
|
||||||
outputPath = resolve(routeDir, "index.html");
|
outputPath = resolve(routeDir, "index.html");
|
||||||
}
|
}
|
||||||
|
|
||||||
writeFileSync(outputPath, html);
|
writeFileSync(outputPath, html);
|
||||||
console.log(`✅ ${route.path} → ${outputPath}`);
|
console.log(`✅ ${route.path}`);
|
||||||
successCount++;
|
successCount++;
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`❌ Ошибка при генерации ${route.path}:`, error.message);
|
console.error(`❌ Ошибка при генерации ${route.path}:`, error.message);
|
||||||
errorCount++;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const urls = allRoutes.map(route => ({
|
const urls = routes.map(route => ({
|
||||||
url: route.path,
|
url: route.path,
|
||||||
lastmod: new Date().toISOString().split('T')[0],
|
lastmod: new Date().toISOString().split('T')[0],
|
||||||
priority: route.path === '/' ? 1.0 : route.path.startsWith('/product') ? 0.8 : 0.7
|
priority: route.path === '/' ? 1.0 : route.path.startsWith('/product') ? 0.8 : 0.7
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const sitemapContent = generateSitemap(urls, 'https://pvh.projects.rustprogersteam.ru'); // замените на ваш домен
|
|
||||||
const sitemapPath = resolve(distPath, 'sitemap.xml');
|
|
||||||
writeFileSync(sitemapPath, sitemapContent);
|
|
||||||
console.log(`\n🗺️ Sitemap создан: ${sitemapPath}`);
|
|
||||||
|
|
||||||
|
|
||||||
// ============================================
|
|
||||||
// ВСПОМОГАТЕЛЬНЫЕ ФУНКЦИИ
|
|
||||||
// ============================================
|
|
||||||
|
|
||||||
function escapeHtml(text) {
|
|
||||||
return text
|
|
||||||
.replace(/&/g, '&')
|
|
||||||
.replace(/</g, '<')
|
|
||||||
.replace(/>/g, '>')
|
|
||||||
.replace(/"/g, '"')
|
|
||||||
.replace(/'/g, ''');
|
|
||||||
}
|
|
||||||
|
|
||||||
function generateSitemap(urls, baseUrl) {
|
function generateSitemap(urls, baseUrl) {
|
||||||
return `<?xml version="1.0" encoding="UTF-8"?>
|
return `<?xml version="1.0" encoding="UTF-8"?>
|
||||||
@@ -143,4 +215,9 @@ ${urls.map(item => ` <url>
|
|||||||
</urlset>`;
|
</urlset>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("✨ Done!");
|
const sitemapContent = generateSitemap(urls, 'https://pvh.projects.rustprogersteam.ru');
|
||||||
|
const sitemapPath = resolve(distPath, 'sitemap.xml');
|
||||||
|
writeFileSync(sitemapPath, sitemapContent);
|
||||||
|
|
||||||
|
console.log(`\n✨ Готово! Сгенерировано ${successCount} страниц.`);
|
||||||
|
console.log(`🗺️ Sitemap: ${sitemapPath}`);
|
||||||
Reference in new issue
Block a user