Создание Собственной Рассылки с Claude

Второй день всплеска трафика с Hacker News. Сорок тысяч посетителей, никакого способа связаться с ними снова. Мне нужна была форма подписки на рассылку.

Я посмотрел на Buttondown, Beehiiv, Substack, ConvertKit. Все излишне сложное. Мне нужно было только собирать электронные адреса. Мне не нужны были кампании, аналитика или управление подписчиками. И я хотел владеть своими данными.

Поэтому я попросил Claude построить это.

flowchart LR
    User([User]) --> Form[Newsletter Form]
    Form -->|POST /api/subscribe| Worker[Cloudflare Worker]
    Worker --> KV[(Cloudflare KV)]
    KV -.->|Daily sync| GHA[GitHub Actions]
    GHA -.-> Repo[(subscribers.jsonl)]
flowchart TB
    User([User]) --> Form[Newsletter Form]
    Form -->|POST /api/subscribe| Worker[Cloudflare Worker]
    Worker --> KV[(Cloudflare KV)]
    KV -.->|Daily sync| GHA[GitHub Actions]
    GHA -.-> Repo[(subscribers.jsonl)]

Настройка

Я дал Claude токен Cloudflare API через переменную окружения и описал, что я хочу: форму, которая собирает электронные адреса и хранит их где-то, где я контролирую.

Менее чем через 30 минут у меня была работающая рассылка.

Что Построил Claude

КомпонентТехнология
ФормаHTML + vanilla JS
БэкендCloudflare Worker
ХранилищеCloudflare KV
СинхронизацияGitHub Actions

Форма — это частичный шаблон Hugo, который отправляет на /api/subscribe:

<form id="newsletter-form">
  <input type="email" name="email" placeholder="[email protected]" required>
  <button type="submit">Subscribe</button>
</form>

Бэкенд — это Cloudflare Worker из 42 строк, который обрабатывает валидацию, нормализацию, обнаружение дубликатов и санитизацию реферера:

export async function onRequestPost(context) {
  const { email } = await request.json();

  // Validate, normalize, check for duplicates
  const emailKey = email.trim().toLowerCase();
  const existing = await env.SUBSCRIBERS.get(emailKey);
  if (existing) {
    return Response.json({ success: true, message: "Already subscribed" });
  }

  // Sanitize referer (strip query params for privacy)
  const referer = request.headers.get("Referer");
  const source = referer ? new URL(referer).origin + new URL(referer).pathname : "direct";

  // Store in KV with metadata
  await env.SUBSCRIBERS.put(emailKey, JSON.stringify({
    subscribedAt: new Date().toISOString(),
    source
  }));

  return Response.json({ success: true }, { status: 201 });
}

Ограничение скорости исходит от встроенной защиты Cloudflare — дополнительный код не нужен.

Рабочий процесс GitHub Actions запускается ежедневно для синхронизации подписчиков из Cloudflare KV в файл JSONL в репозитории. Мой список подписчиков живет в системе контроля версий. Если Cloudflare завтра закроет мой аккаунт, у меня все еще есть мои данные. И они в формате, с которым LLM могут легко работать.

Рабочий Процесс

Claude итерировал локально, пока все не заработало — отправка формы, хранилище KV, обработка ошибок. Затем отправил в предварительную версию приложения, протестировал полный поток и слил с main.

Форма вышла в продакшен, пока трафик все еще шел.

Все это было построено за одну сессию.

Почему Не SaaS?

Buttondown отлично подходит, если вам это нужно. Мне нет. Мои требования:

  1. Собирать электронные адреса
  2. Хранить их где-то, где я контролирую
  3. Быстро развернуть

Вот и все. Мне не нужны капельные кампании или A/B-тестирование или причудливые шаблоны. Мне нужен список адресов электронной почты, который принадлежит мне.

42 строки кода вместо еще одной месячной подписки. Иногда простое решение — правильное.

Вывод: несовершенное и живое бьет совершенное и запланированное. Я мог бы потратить часы на оценку сервисов рассылок, сравнение функций, чтение отзывов. Вместо этого я развернул что-то за 30 минут и двинулся дальше.