admin管理员组

文章数量:1357377

sitemap generation, in dev mode it displays normally. In production when opening the page /sitemap.xml - error 500

server console " 0|front | ⨯ [TypeError: Cannot convert argument to a ByteString because the character at index 131 has a value of 1082 which is greater than 255.] " during assembly in the console "sitemap length 606 "

\src\app\sitemap.ts

import { MetadataRoute } from 'next';

import { getCities } from '@/utils/api/request/cities';
import { getAppConfig } from '@/utils/api/config';
import { getAllPoints } from '@/utils/api/request/points';
import { getAllProducts, filterProductsByMenuIds } from '@/utils/api/request/products';
import { getAllShowcases } from '@/utils/api/request/showcases';
import { createPointsByCityMap } from '@/utils/createPointsByCityMap';
import { BASE_SITEMAP_URL, INCLUDED_LINK_KEYS, LINKS } from '@/app/constants';

function createSitemapEntry(
  url: string,
  changeFrequency: MetadataRoute.Sitemap[0]['changeFrequency'],
  priority: number,
): MetadataRoute.Sitemap[0] {
  const encodedUrl = encodeURIComponent(url);

  return {
    url: encodedUrl,
    lastModified: new Date(),
    changeFrequency,
    priority,
  };
}

export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
  try {
    const [configData, citiesData] = await Promise.all([getAppConfig(), getCities()]);

    if (
      !configData?.data?.CombinationOfParameters ||
      !citiesData?.data ||
      !configData.data?.TypeMenu?.Object
    ) {
      throw new Error('Missing required data');
    }

    const points = await getAllPoints(citiesData);
    const cities = citiesData.data;
    const menu = configData.data.TypeMenu;

    const pointsByCityMap = createPointsByCityMap(cities, points);
    const allProducts = await getAllProducts(
      cities,
      pointsByCityMap,
      configData.data.CombinationOfParameters,
    );

    const sitemapEntries: MetadataRoute.Sitemap = [
      createSitemapEntry(BASE_SITEMAP_URL, 'daily', 1),
    ];

    for (const city of cities) {
      const cityUrl = `${BASE_SITEMAP_URL}/${city.Url}`;
      sitemapEntries.push(createSitemapEntry(cityUrl, 'weekly', 1));

      INCLUDED_LINK_KEYS.forEach((key) => {
        const link = LINKS[key];

        if (typeof link === 'function') {
          sitemapEntries.push(
            createSitemapEntry(`${BASE_SITEMAP_URL}${link(city.Url)}`, 'weekly', 0.5),
          );
        } else if (typeof link === 'string') {
          sitemapEntries.push(createSitemapEntry(`${BASE_SITEMAP_URL}${link}`, 'weekly', 0.5));
        } else if (typeof link === 'object') {
          Object.entries(link).forEach(([, childLink]) => {
            if (typeof childLink === 'function') {
              sitemapEntries.push(
                createSitemapEntry(`${BASE_SITEMAP_URL}${childLink(city.Url)}`, 'weekly', 0.5),
              );
            }
          });
        }
      });

      for (const category of menu.Object) {
        const categoryUrl = `${BASE_SITEMAP_URL}/${city.Url}/${category.URL}`;
        sitemapEntries.push(createSitemapEntry(categoryUrl, 'weekly', 0.8));

        const menuIds = category.TypeIDs?.map((typeID) => typeID.ID) || [];
        const isShowcase = category.Showcase;

        const products = isShowcase
          ? await getAllShowcases([city], pointsByCityMap, allProducts)
          : filterProductsByMenuIds(allProducts, menuIds);

        Object.entries(products).forEach(([cityUrl, pointsData]) => {
          Object.values(pointsData).forEach((productArrays) => {
            productArrays?.flat().forEach((product) => {
              sitemapEntries.push(
                createSitemapEntry(
                  `${BASE_SITEMAP_URL}/${cityUrl}/${category.URL}/${product.NameTranslit}`,
                  'weekly',
                  0.9,
                ),
              );
            });
          });
        });
      }
    }

    const uniqueSitemapEntries = Array.from(
      new Map(sitemapEntries.map((entry) => [entry.url, entry])).values(),
    );

    console.log('sitemap length', uniqueSitemapEntries?.length);

    return uniqueSitemapEntries;
  } catch (error) {
    console.error('Error generating sitemap:', error);
    return [createSitemapEntry(BASE_SITEMAP_URL, 'daily', 1)];
  }
}

{
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "dev": "npm run clean && cross-env AUTOPREFIXER_GRID=autoplace NODE_EXTRA_CA_CERTS=certs/CAROOT.crt next dev --turbo",
    "prebuild": "npm run clear-tags && npm run clean",
    "build": "cross-env AUTOPREFIXER_GRID=autoplace NODE_EXTRA_CA_CERTS=certs/CAROOT.crt next build",
    "postbuild": "npm run clean-tags",
    "start": "cross-env AUTOPREFIXER_GRID=autoplace NODE_EXTRA_CA_CERTS=certs/CAROOT.crt next start",
    "lint": "next lint",
    "clean": "npx rimraf .next out",
    "clear-tags": "node ./src/utils/revalidate/clearTags.mjs",
    "clean-tags": "node ./src/utils/revalidate/cleanTags.mjs"
  },
  "dependencies": {
    "@emotion/react": "^11.11.4",
    "@emotion/styled": "^11.11.5",
    "@pbe/react-yandex-maps": "^1.2.5",
    "@reduxjs/toolkit": "^2.3.0",
    "@types/react": "19.0.0",
    "@types/react-dom": "19.0.0",
    "caniuse-lite": "^1.0.30001699",
    "date-fns": "^4.1.0",
    "date-fns-tz": "^3.2.0",
    "eslint-plugin-compat": "^5.0.0",
    "eslint-plugin-prettier": "^5.1.3",
    "iron-session": "^8.0.4",
    "moment": "^2.30.1",
    "moment-timezone": "^0.5.45",
    "next": "^15.2.3",
    "nextjs-toploader": "^3.7.15",
    "postcss-flexbugs-fixes": "^5.0.2",
    "postcss-preset-env": "^9.6.0",
    "react": "^19.0.0",
    "react-datepicker": "^7.5.0",
    "react-dom": "^19.0.0",
    "react-google-recaptcha": "^3.1.0",
    "react-insta-stories": "^2.7.0",
    "react-masonry-css": "^1.0.16",
    "react-perfect-scrollbar": "^1.5.8",
    "react-phone-input-2": "^2.15.1",
    "react-photo-view": "^1.2.6",
    "react-range": "^1.10.0",
    "react-redux": "^9.1.2",
    "react-select": "^5.8.3",
    "swiper": "^11.1.4",
    "transliteration": "^2.3.5",
    "use-media": "^1.5.0"
  },
  "devDependencies": {
    "@types/node": "^20",
    "@types/react-google-recaptcha": "^2.1.9",
    "@types/yandex-maps": "^2.1.36",
    "@typescript-eslint/eslint-plugin": "^8.24.0",
    "@typescript-eslint/parser": "^8.24.0",
    "autoprefixer": "^10.4.19",
    "cross-env": "^7.0.3",
    "eslint": "^8",
    "eslint-config-next": "^15.0.3",
    "eslint-config-prettier": "^9.1.0",
    "eslint-plugin-css-modules": "^2.12.0",
    "prettier": "^3.5.0",
    "typescript": "^5.4.5",
    "typescript-plugin-css-modules": "^5.1.0"
  }
}

node v20.15.1

I expect a list of sitemaps with the ability to revalidate by time or tag or path

I tried to do it through the route, like here .js/discussions/50419

\src\app\api\ppp.xml\route.ts

import { getCities } from '@/utils/api/request/cities';
import { getAppConfig } from '@/utils/api/config';
import { getAllPoints } from '@/utils/api/request/points';
import { getAllProducts, filterProductsByMenuIds } from '@/utils/api/request/products';
import { getAllShowcases } from '@/utils/api/request/showcases';
import { createPointsByCityMap } from '@/utils/createPointsByCityMap';
import { BASE_SITEMAP_URL, LINKS } from '@/app/constants';

const INCLUDED_LINK_KEYS: (keyof typeof LINKS)[] = [
  'cashback',
  'contacts',
  'stock',
  'law',
  'lawChilds',
  'b2b',
  'help',
];

function createSitemapEntry(url: string, changefreq: string, priority: number): string {
  const encodedUrl = encodeURIComponent(url); // Кодируем URL
  return `
    <url>
      <loc>${encodedUrl}</loc>
      <lastmod>${new Date().toISOString()}</lastmod>
      <changefreq>${changefreq}</changefreq>
      <priority>${priority}</priority>
    </url>
  `;
}

export async function GET(): Promise<Response> {
  try {
    const [configData, citiesData] = await Promise.all([getAppConfig(), getCities()]);

    if (
      !configData?.data?.CombinationOfParameters ||
      !citiesData?.data ||
      !configData.data?.TypeMenu?.Object
    ) {
      throw new Error('Missing required data');
    }

    const points = await getAllPoints(citiesData);
    const cities = citiesData.data;
    const menu = configData.data.TypeMenu;

    const pointsByCityMap = createPointsByCityMap(cities, points);
    const allProducts = await getAllProducts(
      cities,
      pointsByCityMap,
      configData.data.CombinationOfParameters,
    );

    let sitemapEntries = createSitemapEntry(BASE_SITEMAP_URL, 'daily', 1.0);

    for (const city of cities) {
      const cityUrl = `${BASE_SITEMAP_URL}/${city.Url}`;
      sitemapEntries += createSitemapEntry(cityUrl, 'weekly', 1.0);

      INCLUDED_LINK_KEYS.forEach((key) => {
        const link = LINKS[key];

        if (typeof link === 'function') {
          sitemapEntries += createSitemapEntry(
            `${BASE_SITEMAP_URL}${link(city.Url)}`,
            'weekly',
            0.5,
          );
        } else if (typeof link === 'string') {
          sitemapEntries += createSitemapEntry(`${BASE_SITEMAP_URL}${link}`, 'weekly', 0.5);
        } else if (typeof link === 'object') {
          Object.entries(link).forEach(([, childLink]) => {
            if (typeof childLink === 'function') {
              sitemapEntries += createSitemapEntry(
                `${BASE_SITEMAP_URL}${childLink(city.Url)}`,
                'weekly',
                0.5,
              );
            }
          });
        }
      });

      for (const category of menu.Object) {
        const categoryUrl = `${cityUrl}/${category.URL}`;
        sitemapEntries += createSitemapEntry(categoryUrl, 'weekly', 0.8);

        const menuIds = category.TypeIDs?.map((typeID) => typeID.ID) || [];
        const isShowcase = category.Showcase;

        const products = isShowcase
          ? await getAllShowcases([city], pointsByCityMap, allProducts)
          : filterProductsByMenuIds(allProducts, menuIds);

        Object.entries(products).forEach(([cityUrl, pointsData]) => {
          Object.values(pointsData).forEach((productArrays) => {
            productArrays?.flat().forEach((product) => {
              sitemapEntries += createSitemapEntry(
                `${BASE_SITEMAP_URL}/${cityUrl}/${category.URL}/${product.NameTranslit}`,
                'weekly',
                0.9,
              );
            });
          });
        });
      }
    }

    const xml = `<?xml version="1.0" encoding="UTF-8"?>
      <urlset xmlns=".9">
        ${sitemapEntries}
      </urlset>`;

    const xmlBuffer = Buffer.from(xml, 'utf-8'); // Преобразуем строку в Buffer с кодировкой UTF-8

    const headers = new Headers();
    headers.set('Content-Type', 'application/xml; charset=UTF-8'); // Устанавливаем кодировку в заголовках

    return new Response(xmlBuffer, { headers });
  } catch (error) {
    console.error('Error generating sitemap:', error);
    return new Response('<?xml version="1.0" encoding="UTF-8"?><urlset></urlset>', {
      headers: { 'Content-Type': 'application/xml; charset=UTF-8' },
    });
  }
}

works, but if I add cache - export const revalidate = 4000; then the same error "[TypeError: Cannot convert argument to a ByteString because the character at index 131 has a value of 1082 which is greater than 255.] "

本文标签: