feat: скаффолд Astro 5 SSG (главная + /privacy + consent gate)
Some checks failed
deploy / deploy (push) Failing after 14s

- Главная: hero, адрес Люблинская 100 (Аквапарк ФЭНТАЗИ), 4 кликабельных tel:, карта Яндекса
- /privacy: политика 152-ФЗ + ConsentRevoke (отозвать/сбросить)
- Аналитика перенесена 1:1 с WP: Яндекс.Метрика 47169531 (Webvisor) + GA4 GT-WRF7ZZ8
- Скрипты в type=text/plain, активируются после согласия (pit-consent в localStorage+cookie)
- robots.txt с явным Allow для GPTBot/ClaudeBot/PerplexityBot/Google-Extended/CCBot
- llms.txt + ai.txt (spawning.ai стандарт)
- IndexNow ключ 901a779d62ca4702ad810c863b45e1f7
- JSON-LD AutoPartsStore с адресом и 4 телефонами
- nginx:1.29-alpine runtime, контейнер на :4147
- Gitea Actions deploy.yml + Trivy scan + IndexNow ping
This commit is contained in:
Dmitry Gusev
2026-05-22 04:31:55 +03:00
parent 6ff1827690
commit ed27dcfc14
27 changed files with 6099 additions and 1 deletions

75
scripts/indexnow.js Normal file
View File

@@ -0,0 +1,75 @@
// IndexNow: уведомить Yandex/Bing о новых/обновлённых URL.
// Запуск: node scripts/indexnow.js (из CI после деплоя или вручную)
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://pitstopavto.su';
const HOST = 'pitstopavto.su';
const KEY = '901a779d62ca4702ad810c863b45e1f7';
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].trim());
}
let urls = [];
const distDir = path.join(root, 'dist');
const sitemapIndex = path.join(distDir, 'sitemap-index.xml');
if (fs.existsSync(sitemapIndex)) {
const idx = fs.readFileSync(sitemapIndex, 'utf-8');
for (const sub of extractLocs(idx)) {
const fname = sub.split('/').pop();
const localPath = path.join(distDir, fname);
if (fs.existsSync(localPath)) {
urls.push(...extractLocs(fs.readFileSync(localPath, 'utf-8')));
}
}
} else {
console.log('no local sitemap-index.xml, fetching from production…');
try {
const idxResp = await fetch(`${BASE}/sitemap-index.xml`);
if (!idxResp.ok) throw new Error(`HTTP ${idxResp.status}`);
for (const sub of extractLocs(await idxResp.text())) {
const subResp = await fetch(sub);
if (!subResp.ok) continue;
urls.push(...extractLocs(await subResp.text()));
}
} catch (e) {
console.error(`failed to fetch sitemap from ${BASE}: ${e.message}`);
process.exit(1);
}
}
urls = [...new Set(urls)].filter((u) => u.startsWith(BASE));
if (urls.length === 0) { console.error('no URLs collected'); process.exit(1); }
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'),
]);