Уведомления о падении сайта в ВКонтакте и на почту: пошаговая инструкция (HetrixTools + Lovable)
Пошаговая инструкция «под ключ»: как настроить мгновенные уведомления о падении сайта в беседу ВКонтакте и на почту через HetrixTools и вебхук на Lovable — с кодом, типичными ошибками и чеклистом.

Если сайт упал — из-за хостинга, неудачной правки или ошибки в коде — об этом нужно узнать сразу, а не от клиента. В этой статье разберём рабочую схему мониторинга «под ключ»: бесплатный сервис HetrixTools следит за сайтом, а при падении дёргает вебхук, который отправляет уведомление в беседу ВКонтакте. Параллельно письмо уходит на почту. Всю связку можно настроить за час, не имея своего сервера.
Архитектура решения
┌──────────────┐ сайт упал ┌───────────────┐ POST-вебхук ┌──────────────────┐
│ HetrixTools │ ────────────► │ обнаружил │ ──────────────► │ Lovable-проект │
│ (мониторинг) │ │ падение │ │ (приёмник) │
└──────────────┘ └───────────────┘ └────────┬─────────┘
│ │
│ email │ VK API
▼ ▼
Почта (profbuh@…) Беседа ВКонтакте Почему именно так:
- HetrixTools — один из немногих бесплатных мониторов, где вебхук с произвольным URL доступен на бесплатном тарифе. В UptimeRobot обычный Webhook теперь за платным планом, а интеграции Slack/Splunk валидируют домен и не дают вставить свой URL.
- Lovable-проект — это приёмник вебхука, живущий независимо от вашего хостинга. Если упал сам сайт вместе с хостингом, приёмник продолжает работать и сигнал дойдёт. На бесплатного тарифа Lovable (≈20 Cloud-кредитов в месяц) хватает с запасом: вебхук почти не расходует ресурсы.
- ВКонтакте — уведомления приходят в беседу команды через бота сообщества. Callback API при этом настраивать не нужно — мы только отправляем сообщения.
Шаг 1. Проект-приёмник на Lovable
Создайте проект на lovable.dev и попросите агента сделать публичный вебхук-эндпоинт. Проект должен содержать два файла.
1.1. Эндпоинт src/routes/api/public/uptime.ts
Принимает GET и POST, понимает и JSON, и form-urlencoded, приводит разные форматы интеграций к единому виду и проверяет секретный токен в URL:
import { createFileRoute } from "@tanstack/react-router";
import { dispatchAlert, type AlertPayload } from "@/lib/alerts.server";
function timingSafeEqual(a: string, b: string): boolean {
if (a.length !== b.length) return false;
let diff = 0;
for (let i = 0; i < a.length; i++) diff |= a.charCodeAt(i) ^ b.charCodeAt(i);
return diff === 0;
}
function normalizeStatus(raw: string | null): AlertPayload["status"] {
const value = (raw ?? "").toLowerCase();
if (value === "1" || value.includes("down") || value.includes("offline") || value.includes("critical"))
return "down";
if (value === "2" || value.includes("up") || value.includes("online") || value.includes("recover") || value.includes("resolve"))
return "up";
return "unknown";
}
/** Разворачиваем вложенный JSON в плоские параметры — разные сервисы шлют разные ключи. */
function flatten(obj: unknown, params: URLSearchParams, prefix = ""): void {
if (obj == null || typeof obj !== "object") return;
for (const [key, value] of Object.entries(obj as Record<string, unknown>)) {
const name = prefix ? `${prefix}.${key}` : key;
if (value != null && typeof value === "object") flatten(value, params, name);
else if (value != null) params.set(name, String(value));
}
}
async function handle(request: Request): Promise<Response> {
const url = new URL(request.url);
const params = new URLSearchParams(url.search);
if (request.method === "POST") {
const body = await request.text();
if (body) {
const t = body.trimStart();
if (t.startsWith("{") || t.startsWith("[")) {
try { flatten(JSON.parse(body), params); } catch { /* пропускаем битый JSON */ }
} else {
for (const [k, v] of new URLSearchParams(body)) params.set(k, v);
}
}
}
// Проверка токена — без него эндпоинт публично доступен всем.
const expected = process.env["UPTIME_WEBHOOK_TOKEN"];
const provided = params.get("token") ?? request.headers.get("x-webhook-token") ?? "";
if (!expected || !timingSafeEqual(provided, expected)) {
return new Response("Invalid token", { status: 401 });
}
const result = await dispatchAlert(buildPayload(params));
return Response.json({ ok: true, ...result });
}
export const Route = createFileRoute("/api/public/uptime")({
server: { handlers: { GET: ({ request }) => handle(request), POST: ({ request }) => handle(request) } },
}); Обратите внимание на префикс /api/public/ — такие маршруты в Lovable доступны снаружи без авторизации, поэтому проверка токена обязательна.
1.2. Отправка в ВК — src/lib/alerts.server.ts
export async function sendVkAlert(p: AlertPayload): Promise<{ ok: boolean; error?: string }> {
const token = process.env["VK_BOT_TOKEN"];
const peerId = process.env["VK_PEER_ID"];
if (!token || !peerId) return { ok: false, error: "VK_BOT_TOKEN или VK_PEER_ID не заданы" };
const params = new URLSearchParams({
peer_id: peerId,
random_id: String(Date.now() % 2_000_000_000),
message: formatMessage(p), // "🔴 САЙТ НЕ РАБОТАЕТ" + монитор, URL, причина, время
access_token: token,
v: "5.199",
});
const res = await fetch("https://api.vk.com/method/messages.send", {
method: "POST",
headers: { "content-type": "application/x-www-form-urlencoded" },
body: params.toString(),
});
const json = await res.json();
if (json.error) return { ok: false, error: json.error.error_msg };
return { ok: true };
} 1.3. Секреты проекта
В Lovable добавьте три секрета (они доступны только серверному коду, в браузер не попадают):
UPTIME_WEBHOOK_TOKEN— случайная строка, например изopenssl rand -hex 16. Она же будет частью URL вебхука.VK_BOT_TOKEN— токен сообщества ВК (см. шаг 2).VK_PEER_ID— id беседы, куда писать (см. важный подводный камень в шаге 2).
1.4. Публикация
Нажмите Publish в редакторе Lovable. Проект получит постоянный адрес вида https://имя-проекта.lovable.app. Ваш рабочий вебхук:
https://имя-проекта.lovable.app/api/public/uptime?token=ВАШ_ТОКЕН
Важно про аккаунты: проект и его лимиты привязаны к воркспейсу Lovable, а не к URL. Работайте из одного основного аккаунта; если проект создан под другим — пригласите основной email в воркспейс через Share / Workspace Settings. Опубликованный сайт работает независимо от того, залогинены вы или нет; бесплатного тарифа для такого приёмника достаточно.
Шаг 2. Бот ВКонтакте
- В сообществе ВК: Управление → Работа с API → Ключи доступа → Создать ключ. Разрешите права на сообщения сообщества. Это и есть
VK_BOT_TOKEN. - Там же: Сообщения → Настройки для бота — включите «Возможности ботов», добавьте бота в нужную беседу и выдайте ему доступ ко всей переписке (или сделайте администратором беседы).
- Определите
VK_PEER_ID.
Подводный камень №1: peer_id из ссылки — не настоящий
Это главная ошибка, на которой всё встаёт. Ссылка вида vk.com/im?sel=2000000119 или ключ беседы с peer2000000119 показывает id вашего чата, но бот видит беседу под своим номером. На практике: в ссылке было 2000000119, а реальный peer_id оказался 2000000001 — и API отвечал «You don’t have access to this chat».
Решение — диагностический режим в вебхуке. Откройте в браузере:
https://имя-проекта.lovable.app/api/public/uptime?token=ВАШ_ТОКЕН&diag=1
Обработчик вызовет messages.getConversations от имени бота и вернёт список чатов, которые бот реально видит, с их настоящими peer_id:
if (params.get("diag") === "1") {
const list = await call("messages.getConversations", { count: "50" });
const chats = list.response?.items?.map((i) => ({
type: i.conversation?.peer?.type,
peer_id: i.conversation?.peer?.id,
}));
return Response.json({ chats });
} Берёте peer_id нужной беседы (тип chat), обновляете секрет VK_PEER_ID — и всё работает.
Подводный камень №2: Callback API не нужен
В настройках сообщества есть раздел «Callback API» с сервером и строкой подтверждения. Для нашей схемы он не используется: мы не принимаем события от ВК, а только отправляем сообщения методом messages.send с токеном сообщества. Ничего там настраивать и подтверждать не требуется.
Шаг 3. Мониторинг в HetrixTools
- Зарегистрируйтесь на hetrixtools.com (бесплатный тариф).
- Uptime Monitors → Add Monitor → Website Monitor: URL —
https://ваш-сайт.ru/, метод GET, интервал 1–5 минут. - Contact Lists → Add Contact List. Внутри списка добавьте контакт типа Webhook: вставьте URL из шага 1.4, метод POST. Выбирать формат JSON не обязательно — наш обработчик принимает любые поля и сам находит статус.
- В этот же контакт-лист добавьте контакт типа Email с адресом для уведомлений. На бесплатном тарифе к монитору можно привязать только один контакт-лист — поэтому вебхук и почта должны жить в одном списке вместе.
- Привяжите контакт-лист к монитору в настройках монитора (поле Contact List).
- Если HetrixTools пришлёт письмо для подтверждения email-контакта — перейдите по ссылке из него.
Шаг 4. UptimeRobot — как второй монитор на почту
UptimeRobot оставьте как независимый запасной канал: HTTP(s)-монитор на тот же URL и родной Email-алерт. Но учтите его текущие ограничения бесплатного плана:
- тип Alert Contact Webhook доступен только на платном тарифе («Upgrade to access»);
- интеграции Slack, Discord, Splunk (VictorOps) валидируют домен URL — вставить произвольный адрес не получится (Splunk, например, требует, чтобы URL начинался с
https://alert.victorops.com/).
Поэтому связка «вебхук → ВК» живёт на HetrixTools, а UptimeRobot дублирует падение на почту — два независимых сервиса снижают шанс пропустить инцидент.
Шаг 5. Проверка
Не ждите реального падения — пошлите тестовый алерт вручную:
curl -X POST "https://имя-проекта.lovable.app/api/public/uptime?token=ВАШ_ТОКЕН" \
-H "content-type: application/json" \
-d '{"monitor_status":"offline","monitor_name":"Тест","monitor_target":"https://ваш-сайт.ru/","monitor_errors":"Connection timeout"}' Правильный ответ: {"ok":true,"vk":{"ok":true}} — и сообщение «🔴 САЙТ НЕ РАБОТАЕТ» в беседе ВК. Если в ответе vk.error — читайте текст ошибки VK API: чаще всего это неверный peer_id (шаг 2) или бот не добавлен в беседу.
Типичные ошибки и их признаки
| Симптом | Причина | Решение |
|---|---|---|
You don't have access to this chat | peer_id из ссылки не совпадает с id, под которым бот видит беседу | Открыть ?diag=1 и взять реальный peer_id из списка бесед бота |
VK API: messages are disabled / нет прав | Токен создан без прав на сообщения или бот не в беседе | Пересоздать ключ с правом «сообщения», добавить бота в беседу и дать доступ к переписке |
| Вебхук отвечает 401 | Токен в URL не совпадает с секретом UPTIME_WEBHOOK_TOKEN | Сверить токен в секрете и в URL монитора |
| В HetrixTools привязан только вебхук, письма не приходят | На бесплатном тарифе один контакт-лист на монитор, email в него не добавлен | Добавить email-контакт в тот же список, что и вебхук |
| UptimeRobot не даёт создать Webhook-контакт | Функция закрыта платным планом | Использовать HetrixTools для вебхука, UptimeRobot — для email |
| В статьях/настройках пытаетесь подтвердить Callback API ВК | Не требуется | Callback API не используется — мы только отправляем сообщения |
| Проект «потерялся» после смены аккаунта Lovable | Проекты живут в воркспейсе конкретного аккаунта | Работать из одного основного аккаунта или пригласить его email в воркспейс |
Чеклист «под ключ»
- Lovable: проект с двумя файлами (роут
/api/public/uptime+ отправка в ВК), три секрета, проект опубликован. - ВК: токен сообщества с правом на сообщения, бот в беседе, реальный peer_id получен через
?diag=1и сохранён в секретах. - HetrixTools: Website Monitor на сайт, контакт-лист с Webhook (POST) и Email внутри одного списка, список привязан к монитору, email подтверждён.
- UptimeRobot (опционально): второй монитор с email-алертом.
- Тест: curl-запрос вернул
{"ok":true}, сообщение дошло в ВК, тестовое письмо — на почту.
После этого падение сайта в течение 1–5 минут превращается в сообщение в рабочей беседе ВК и письмо на почту — без своего сервера и на полностью бесплатных тарифах.

