Найден корректный robots.txt
Файл управления сканированием сайта роботами
?
Robots.txt указывает поисковым роботам, какие страницы сканировать, а какие — нет. Ошибки в файле могут случайно закрыть важные разделы от индексации.
Robots.txt настроен корректно. Размер файла: 10472 байт. Загружен за: 0сек.
Проверяемая страница не запрещена в robots.txt.
Цепочка редиректов для файла robots.txt:
http://puls-51.ru/robots.txt
301 MovedPermanently
https://puls-51.ru/robots.txt
200 OK
Показать содержимое robots.txt
<!doctype html>
<html lang="ru">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" />
<!-- Значение токена `--color-brand`. Значилось `#9008D8` — пурпур, которым
проект представлялся системе и который в интерфейсе не встречался
нигде; с Ф3 (02.08.2026) фиолетовый в проекте один. -->
<meta name="theme-color" content="#ffffff" />
<meta name="description" content="Городские события Мурманска: концерты, выставки, лекции, клубы" />
<link rel="icon" type="image/png" sizes="96x96" href="/icons/favicon-96x96.png" />
<link rel="shortcut icon" href="/favicon.ico" />
<link rel="apple-touch-icon" sizes="180x180" href="/icons/apple-touch-icon.png" />
<!-- Имя под значком на домашнем экране iOS: у манифеста iOS его не берёт -->
<meta name="apple-mobile-web-app-title" content="Пульс" />
<title>Пульс51 — афиша Мурманска</title>
<!--
Инлайн-перехватчик клиентских падений (Фаза 2 ТЗ «Сбор клиентских
ошибок»). Ниже — обычный классический скрипт, намеренно не модульный,
по двум причинам: модульный скрипт откладывается до конца разбора
документа (не успел бы зарегистрироваться раньше падения основного
бандла) и вдобавок утягивается Vite в граф модулей — то есть попал бы в
сборку и перестал быть тем, чем задуман: обработчиком, живущим отдельно
от кода, который может не разобраться или не выполниться. Если
`index-*.js` падает при разборе или выполнении, этот скрипт уже
отработал и слушает.
Синтаксис — ES5 (var, function, без опционального доступа через точку,
без стрелок и шаблонных строк): если инлайн-скрипт не разберётся
парсером на старом движке, у нас не будет ни ошибок, ни сведений о том,
почему их нет.
ac54c18 и https://api.puls-51.ru подставляются плагином
`transformIndexHtml` в `vite.config.js` при сборке: `define` Vite сюда
не доходит (он работает по JS-графу модулей, а этот скрипт вне графа),
и значение `import.meta.env` инлайн-скрипту тоже недоступно.
-->
<script>
try {
var PULS_ENDPOINT = 'https://api.puls-51.ru' + '/client-errors';
var PULS_BUILD = 'ac54c18';
var PULS_MAX_SENDS = 5;
var pulsSentSignatures = {};
var pulsSentCount = 0;
// Единственная отправляющая функция, выставленная наружу одним
// глобальным именем — ею же в Фазе 3 пользуется ErrorBoundary.
// Второй копии кода отправки в проекте быть не должно.
window.__pulsReportError = function (kind, message, stack) {
try {
if (pulsSentCount >= PULS_MAX_SENDS) return;
var safeMessage = message === undefined || message === null ? 'Unknown error' : String(message);
var safeStack = stack ? String(stack) : '';
// Дедупликация по сигнатуре «сообщение + первые 200 символов
// стека»: повторная та же ошибка (например, падающая на каждом
// клике) не должна слать пачку одинаковых сообщений.
var signature = safeMessage + '|' + safeStack.slice(0, 200);
if (pulsSentSignatures[signature]) return;
pulsSentSignatures[signature] = true;
pulsSentCount += 1;
var payload = {
kind: kind,
message: safeMessage.slice(0, 2000),
url: location.href,
build: PULS_BUILD
};
if (safeStack) {
payload.stack = safeStack.slice(0, 4000);
}
var body = JSON.stringify(payload);
var sent = false;
// sendBeacon переживает выгрузку страницы — важно для падений,
// за которыми сразу следует переход или закрытие вкладки.
try {
if (navigator.sendBeacon) {
var blob = new Blob([body], { type: 'application/json' });
sent = navigator.sendBeacon(PULS_ENDPOINT, blob);
}
} catch (beaconError) {
sent = false;
}
if (!sent) {
try {
fetch(PULS_ENDPOINT, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: body,
keepalive: true,
mode: 'cors'
}).catch(function () {
// сеть отказала — сведения об ошибке теряются, но
// обработчик ошибок не должен породить вторую ошибку
});
} catch (fetchError) {
// отправить нечем — молчим
}
}
} catch (sendError) {
// ошибка в обработчике ошибок — худший из возможных исходов
}
};
// Захват на фазе capture (третий аргумент true) — без него события
// несостоявшейся загрузки ресурса (SCRIPT/LINK) не дошли бы: они не
// всплывают и видны только при перехвате сверху вниз.
window.addEventListener('error', function (event) {
try {
var target = event && event.target;
if (target && target.tagName) {
// Несостоявшаяся загрузка ресурса. Дальше идут только SCRIPT и
// LINK — главный подозреваемый в текущем баге (ленивый чанк,
// которого нет на диске после rsync --delete) и стили/шрифты,
// от которых зависит разметка. IMG сюда намеренно не входит:
// битая картинка (аватар, обложка, потерянный файл в media/) —
// обычный шум содержимого, а не падение приложения, и он
// вдвойне дорогой — съедает потолок в 5 отправок за загрузку
// страницы и лимит сервера 20 сообщений за 5 минут на адрес, а
// за одним адресом мобильного оператора сидят тысячи людей.
if (target.tagName === 'SCRIPT' || target.tagName === 'LINK') {
var resourceUrl = target.src || target.href || '(адрес неизвестен)';
window.__pulsReportError('chunk-load', 'Не удалось загрузить ресурс: ' + resourceUrl);
}
return;
}
var err = event && event.error;
var message = (err && err.message) || (event && event.message) || 'Unknown error';
var stack = err && err.stack;
window.__pulsReportError('error', message, stack);
} catch (handlerError) {
// обработчик не должен падать сам
}
}, true);
window.addEventListener('unhandledrejection', function (event) {
try {
var reason = event && event.reason;
var message = (reason && reason.message) || String(reason);
var stack = reason && reason.stack;
window.__pulsReportError('unhandledrejection', message, stack);
} catch (handlerError) {
// обработчик не должен падать сам
}
});
// Сторож монтирования. Главный симптом, ради которого написано всё
// ТЗ, — вечная загрузка БЕЗ единого исключения: статика и API
// отвечают 200, устройство просто перестаёт слать запросы. Ни один
// слушатель выше его не увидит: `error` не сработал, промис не
// отклонён, React не смонтировался — значит и ErrorBoundary из
// Фазы 3 тоже молчит. Без сторожа мы получаем ноль сообщений от
// пострадавших и возвращаемся к гаданию.
//
// 12 секунд — это не «сколько обычно грузится сайт», а порог, после
// которого молчание уже не тянет на «медленно», а тянет на
// «не смонтировалось».
var PULS_MOUNT_TIMEOUT_MS = 12000;
var pulsMountConfirmed = false;
var pulsMountWatchdogTimer = setTimeout(function () {
try {
if (pulsMountConfirmed) return;
// В сообщение идёт только то, что различает гипотезы — оно
// обрезается 500 знаками на сервере, длинное потеряет хвост.
var rootEl = document.getElementById('root');
var rootHasContent = !!(rootEl && rootEl.firstChild);
var online = navigator.onLine;
var swController = !!(navigator.serviceWorker && navigator.serviceWorker.controller);
var scripts = document.getElementsByTagName('script');
var mainBundlePresent = false;
for (var i = 0; i < scripts.length; i++) {
if (scripts[i].type === 'module' && scripts[i].src) {
mainBundlePresent = true;
break;
}
}
var message = 'Mount watchdog: приложение не подтвердило монтирование за ' +
PULS_MOUNT_TIMEOUT_MS + 'мс' +
'; root=' + (rootHasContent ? 'has-content' : 'empty') +
'; online=' + (online ? 'yes' : 'no') +
'; swController=' + (swController ? 'yes' : 'no') +
'; scripts=' + scripts.length +
'; mainBundleInDom=' + (mainBundlePresent ? 'yes' : 'no');
window.__pulsReportError('error', message);
} catch (watchdogError) {
// сторож не должен падать сам
}
}, PULS_MOUNT_TIMEOUT_MS);
// Второе глобальное имя рядом с отправителем — явная отмена из
// main.jsx сразу после createRoot(...).render(...). Проверка «в
// #root что-то есть» как единственный признак не годится: она
// отвечает на другой вопрос («DOM заполнен») и промолчит, если React
// отрисовал пустое поддерево.
window.__pulsConfirmMount = function () {
try {
pulsMountConfirmed = true;
clearTimeout(pulsMountWatchdogTimer);
} catch (confirmError) {
// не должен падать
}
};
} catch (outerError) {
// весь скрипт целиком в try/catch — падение здесь не должно ронять
// страницу и не должно мешать основному бандлу загрузиться следом
}
</script>
<script type="module" crossorigin src="/assets/index-BHm889fD.js"></script>
<link rel="modulepreload" crossorigin href="/assets/map-_f5vlWdH.js">
<link rel="modulepreload" crossorigin href="/assets/plural-BRH9QrpV.js">
<link rel="stylesheet" crossorigin href="/assets/index-DYlBhC6j.css">
<link rel="manifest" href="/manifest.webmanifest"></head>
<body>
<div id="root"></div>
</body>
</html>