5 элементов порядка: как мы автоматизировали хаос в клининге (Next.js, Telegram и Cloudflare R2 без SDK)

Управление выездными сотрудниками — это ад. Диспетчер пишет в мессенджер, ждёт ответа, вызванивает пропавших, собирает фотки «до» и «после» по личным чатам. Если клинер утром заболел — туши свет, горим. Знакомая картина?

Мы устали от этого ручного привода. Потребовался инструмент, который уберет телефонные звонки из цепочки вообще. В итоге собрали собственную SaaS-платформу. Делюсь реальной архитектурой, тем, как мы отказались от тяжелых SDK, и выстроили разработку на основе знаменитых «пяти элементов».

▍ Первый элемент: Умение рассказывать историю (Проблема и интерфейс)

Любой продукт начинается с боли. Главная проблема рынка — исполнители ненавидят сложные корпоративные приложения. Их никто не хочет скачивать, они тормозят и путают людей.

Мы пошли радикально простым путем. Для менеджеров сделали строгую канбан-доску на Next.js. А для клинеров — Telegram-бота. Но бота хитрого: никакого ввода текста с клавиатуры. Вообще.

Все 90% взаимодействия — это инлайн-кнопки. «Принять», «Начать работу», «Загрузить фото». Бот сам ведет человека за руку: мультиязычно (RU/EN/GE) пингует по утрам и требует фотоотчеты прямо с объекта. Проще некуда.

5 элементов порядка: как мы автоматизировали хаос в клининге (Next.js, Telegram и Cloudflare R2 без SDK)
5 элементов порядка: как мы автоматизировали хаос в клининге (Next.js, Telegram и Cloudflare R2 без SDK)

▍ Элемент второй: Читайте много, выбирайте прагматично (Архитектура)

Прежде чем писать код, мы перебрали десятки вариантов стека. Остановились на надежной классике, но с одной важной особенностью:

  • Frontend: Next.js (TypeScript) и Tailwind CSS. Строгий минимализм.
  • Backend: Node.js (Express) + Knex (SQLite для локальной разработки и PostgreSQL для продакшена).
  • Хранилище (Самое интересное): Фотографии с уборок весят тонну. Сначала думали про Google Drive, но остановились на Cloudflare R2 (у них бесплатный egress-трафик, что критично для медиа).

И тут фишка: чтобы грузить файлы в S3-совместимое хранилище, все обычно ставят официальный @aws-sdk/client-s3. Но зачем нам тянуть мегабайты зависимостей ради одного PUT-запроса? Мы написали свою реализацию AWS Signature V4 на чистых модулях Node.js (crypto и https).

▍ Элемент третий: Просто садитесь и пишите (Код и БД)

Основа системы — избавление менеджера от рутины. Когда падает новый заказ, система:

  1. Ищет свободных клинеров, кто сейчас онлайн.
  2. Кидает заявку в Telegram первому по очереди.
  3. Дает 20 минут на подумать. Если тишина или отказ — пушит следующему.

А вот как выглядит та самая хардкорная отправка фото в Cloudflare R2 без SDK. Линтер учтен, лишних абстракций нет:

// Фрагмент из нашего utils/r2.js const https = require("https"); const crypto = require("crypto"); // Чистый Node.js: формирование Canonical Request и подпись AWS Sig V4 function r2Put(key, body, contentType = "image/jpeg") { return new Promise((resolve, reject) => { const { date, amzDate } = nowUTC(); const payloadHash = sha256hex(body); // ... (Сборка canonical headers) const sig = hmacHex(signingKey(R2_SECRET_KEY, date), stringToSign); const authHdr = `AWS4-HMAC-SHA256 Credential=${R2_ACCESS_KEY}/${scope}, SignedHeaders=${signedHdrs}, Signature=${sig}`; const req = https.request({ hostname: R2_HOST, method: "PUT", path: `/${R2_BUCKET}/${encodeKey(key)}`, headers: { "Authorization": authHdr, "x-amz-date": amzDate, "x-amz-content-sha256": payloadHash, "Content-Length": String(body.length), "Content-Type": contentType } }, (res) => { if (res.statusCode >= 200 && res.statusCode < 300) resolve(); else reject(new Error(`R2 Error: ${res.statusCode}`)); }); req.write(body); req.end(); }); }

Около 300 строк кода на весь S3-клиент (включая генерацию Presigned URL), и мы полностью независимы от причуд чужих библиотек.

▍ Элемент четвёртый: Уделите внимание идее (Бизнес-логика)

Суть хорошей автоматизации — провести заказ от заявки до оплаты, попутно закрывая все edge-кейсы.

Например, когда клинер жмет в боте «Загрузить фото до уборки», происходит сразу несколько вещей под капотом (взгляните на наш index.js):

  1. Файл летит в бакет Cloudflare R2 по безопасному пути.
  2. Сохраняется запись в БД (order_photos).
  3. Если это новый клиент — система автоматически создает его профиль в базе.
  4. Данные тихо синхронизируются в Google Sheets (syncOrderToSheet) для бухгалтерии.

Диспетчер в админке видит уже готовую галерею и закрытый таск. А чтобы диски не пухли вечно, мы написали скрипт очистки cleanupOldPhotos, который раз в полгода тихо удаляет старые фотки уборок из бакета, оставляя только вечные документы клинеров.

5 элементов порядка: как мы автоматизировали хаос в клининге (Next.js, Telegram и Cloudflare R2 без SDK)

▍ Пятый элемент: Готовность к критике и масштабированию

Не бойтесь запускать продукт и ловить фидбек. Мы могли бы взять готовые жирные решения для всего, но выбрали минимализм и самописные интеграции там, где важен контроль (привет, R2 без SDK).

Сложные вещи должны выглядеть просто. Наш подход с канбан-доской и кнопочным Telegram-ботом снял определенно нагрузки с диспетчеров. Настроил ключи, запустил сервис — и оно работает. Пишите код, который решает реальные боли, а не создает новые.
@geronlabs_serg

geronlabs.xyz

  • saas
  • next.js
  • telegram bot
  • node.js
  • cloudflare r2
  • автоматизация бизнеса
  • архитектура
  • aws signature v4
  • white-label
  • клининг
1