Files
striker bd6ab03b2e
All checks were successful
deploy / deploy (push) Successful in 3m7s
feat(seo): discoverability + Schema.org + IndexNow + Trivy (#1)
2026-05-21 14:25:12 +03:00

134 lines
4.1 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// IndexNow: уведомить Yandex/Bing о новых/обновлённых URL.
// Запускается после деплоя из CI или вручную:
// node scripts/indexnow.js
//
// Источник URL — sitemap. Сначала пробуем локальный dist/sitemap*.xml
// (если запускаем сразу после `npm run build`), иначе тянем
// https://anotherreflections.ru/sitemap-index.xml через сеть.
//
// IndexNow ключ хранится в файле public/<key>.txt — он должен быть доступен
// по тому же URL что и сайт, чтобы поисковики могли подтвердить ownership.
import fs from 'node:fs';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const root = path.resolve(__dirname, '..');
const BASE = 'https://anotherreflections.ru';
const HOST = 'anotherreflections.ru';
const KEY = '15455d9f2c7b473bb04336055b792ec9'; // 32-char hex
// Создать key-файл если кто-то его удалил.
const keyFile = path.join(root, 'public', `${KEY}.txt`);
if (!fs.existsSync(keyFile)) {
fs.writeFileSync(keyFile, KEY, 'utf-8');
console.log(`created key file: public/${KEY}.txt`);
}
function extractLocs(xml) {
return [...xml.matchAll(/<loc>([^<]+)<\/loc>/g)].map((m) => m[1]);
}
async function fetchText(url) {
const r = await fetch(url);
if (!r.ok) throw new Error(`HTTP ${r.status} for ${url}`);
return r.text();
}
async function collectFromRemoteIndex(indexUrl) {
const indexXml = await fetchText(indexUrl);
const locs = extractLocs(indexXml);
// Если это sitemap-index — locs указывают на дочерние sitemap-N.xml.
// Если это обычный sitemap — locs уже URL'ы страниц.
const looksLikeIndex = /<sitemapindex/i.test(indexXml);
if (!looksLikeIndex) return locs;
const childUrls = [];
for (const loc of locs) {
try {
const childXml = await fetchText(loc);
childUrls.push(...extractLocs(childXml));
} catch (e) {
console.error(` skip ${loc}: ${e.message}`);
}
}
return childUrls;
}
function collectFromLocalDist() {
const distDir = path.join(root, 'dist');
if (!fs.existsSync(distDir)) return null;
const sitemapFiles = fs
.readdirSync(distDir)
.filter((f) => /^sitemap-\d+\.xml$/.test(f));
let urls = [];
if (sitemapFiles.length > 0) {
for (const f of sitemapFiles) {
const xml = fs.readFileSync(path.join(distDir, f), 'utf-8');
urls.push(...extractLocs(xml));
}
return urls;
}
for (const c of ['sitemap-index.xml', 'sitemap.xml']) {
const p = path.join(distDir, c);
if (fs.existsSync(p)) {
const xml = fs.readFileSync(p, 'utf-8');
return extractLocs(xml);
}
}
return null;
}
let urls = collectFromLocalDist();
if (urls === null || urls.length === 0) {
console.log('no local dist/sitemap*.xml — fetching remote sitemap-index.xml');
try {
urls = await collectFromRemoteIndex(`${BASE}/sitemap-index.xml`);
} catch (e) {
console.error(`failed to fetch remote sitemap: ${e.message}`);
process.exit(0);
}
}
// Уникальные URL только нашего хоста.
urls = [...new Set(urls)].filter((u) => {
try {
return new URL(u).host === HOST;
} catch {
return false;
}
});
if (urls.length === 0) {
console.error('no URLs found; nothing to submit');
process.exit(0);
}
console.log(`Submitting ${urls.length} URLs to IndexNow…`);
const payload = {
host: HOST,
key: KEY,
keyLocation: `${BASE}/${KEY}.txt`,
urlList: urls,
};
async function submit(endpoint) {
try {
const r = await fetch(endpoint, {
method: 'POST',
headers: { 'Content-Type': 'application/json; charset=utf-8' },
body: JSON.stringify(payload),
});
console.log(` ${endpoint}: HTTP ${r.status}`);
} catch (e) {
console.error(` ${endpoint}: ${e.message}`);
}
}
await Promise.all([
submit('https://yandex.com/indexnow'),
submit('https://api.indexnow.org/indexnow'),
]);