Путь «код»: форма → сервер

Поток нарисован, путь «код» выбран. Сервер в Амстердаме у тебя уже есть, бот на нём работает. Не хватает двери: адреса в интернете, на который Яндекс Форма сможет прислать заявку. В прогоне на неё ушло пять заданий агенту, пройдём их по порядку.

Чему научишься в этом уроке:
• завести поддомен с записями A и AAAA и проверить их;
• поручить агенту nginx, сертификат и файрвол так, чтобы не потерять доступ к серверу;
• собрать сервис приёма заявок с секретом в адресе и отдельной службой;
• подключить Яндекс Форму и починить приём по журналу.

Что добавится на сервере

  • nginx (произносится «энджин-икс»): администратор на входе. Принимает всех посетителей по адресу и провожает к нужному сервису внутри.
  • Сертификат Let’s Encrypt через программу certbot: бесплатная печать подлинности для HTTPS.
  • ufw: файрвол, то есть вахтёр со списком открытых дверей. Всё, чего нет в списке, закрыто.
  • Сервис приёма kodium-forma: маленькая программа, которая слушает только внутренний адрес 127.0.0.1:8080. Снаружи к ней не достучаться, только через nginx.
Схема сервера в Амстердаме: Яндекс Форма шлёт POST на https://<твой поддомен>/forma/<секрет> → ufw (открыты SSH, 80, 443) → nginx с сертификатом → 127.0.0.1:8080 → kodium-forma; рядом bot-kodium (опрос Telegram). От kodium-forma стрелки к «Telegram владельцу» и «zayavki.xlsx на Яндекс Диске». Ниже снимок панели reg.ru: записи A и AAAA поддомена, адреса скрыты.

Шаг 1. Поддомен и IPv6

Нужен домен, то есть имя сайта. Если у тебя уже есть домен, заведи в нём поддомен, например kodium.твой-домен.ru: это отдельное имя внутри домена, покупать ничего не нужно. Если домена нет, как купить и привязать его, показано в уроке трека 2 про установку на сервер.

Справка Яндекс Форм говорит, что запросы форма шлёт только по IPv6 (новый вид адресов, длинный, с двоеточиями). Поэтому поддомену нужны две записи в DNS (адресной книге интернета): A ведёт на IPv4-адрес сервера, AAAA на IPv6-адрес.

IPv6 на сервере. В прогоне IPv6 включили в панели Timeweb, и сервер получил адрес вида 2a03:…. Где именно кнопка, платно ли это и нужна ли перезагрузка, мы не записали и не проверяли. Сначала посмотри, есть ли адрес уже сейчас: на сервере выполни команду ниже. Нужна строка с пометкой scope global. Если её нет, ищи IPv6 в настройках сети сервера в панели Timeweb или спроси их поддержку.

ip -6 addr show
  1. Узнай оба адреса: IPv4 тот, что ты набираешь в ssh, IPv6 из строки scope global.
  2. Открой панель того, кто обслуживает DNS твоего домена. Обычно это регистратор, где куплен домен; в прогоне это был reg.ru.
  3. Добавь запись типа A: имя kodium, значение IPv4-адрес сервера.
  4. Добавь запись типа AAAA: имя kodium, значение IPv6-адрес сервера.
  5. Подожди, пока записи разойдутся по интернету: обычно до часа.
  6. Проверь в браузере: https://dns.google/resolve?name=<твой поддомен>&type=AAAA и то же с type=A. В ответе в разделе Answer должен стоять твой адрес.

Проверяй так, а не командой nslookup на своём компьютере: в прогоне nslookup на Windows не видел AAAA-запись, хотя DNS Google отдавал её верно.

Честно о прогоне: заявки от Яндекс Форм в итоге пришли по IPv4, а не по IPv6. Справка и практика расходятся. IPv6 и AAAA-запись мы оставили: так приём работает при любом поведении формы.

Шаг 2. Почему не всё сразу

В прогоне первое задание было «всё сразу»: nginx, сертификат, файрвол, сервис, служба, тесты и проверка одним текстом. Через 3 минуты и 8 ходов агент не сделал ничего и спросил, работать по шагам или скриптом. Поэтому дальше задания идут по одному. Если ты всё же отправил большое задание и агент спросил то же самое, ответь ему заданием шага 3.

Шаг 3. nginx, сертификат и файрвол

Выполняй по шагам сам, не спрашивай. Сейчас только пункт 1: на сервере <адрес сервера> поставь nginx и certbot, получи сертификат Let's Encrypt для <твой поддомен> (почта для certbot — не нужна, используй --register-unsafely-without-email), включи ufw с правилами: OpenSSH, 80, 443 (IPv4 и IPv6). ВНИМАНИЕ: сначала разреши OpenSSH, потом включай ufw, иначе потеряем доступ. Пока nginx пусть отдаёт на / простую страницу «Кодиум: приём заявок». В конце покажи: ufw status, curl -I https://<твой поддомен> с сервера и дату окончания сертификата. Бота не трогай.

Главная строка здесь про OpenSSH. Включить файрвол, не разрешив SSH, значит запереть дверь изнутри, оставив ключи снаружи. Флаг --register-unsafely-without-email значит «сертификат без почты для напоминаний». В прогоне шаг занял 3,5 минуты и 10 ходов.

Как читать ответ: HTTP/1.1 200 или HTTP/2 200 в первой строке curl значит «страница открылась». В ufw status напротив OpenSSH, 80 и 443 должно стоять ALLOW дважды: просто и с пометкой (v6).

Шаг 4. nginx и IPv6

После certbot в прогоне nginx слушал только IPv4: запрос curl -6 возвращал 000, то есть «соединиться не удалось». Записи DNS были ни при чём. Задание на починку (текст курса):

curl -6 на https://<твой поддомен> отвечает 000, а по IPv4 всё работает. Похоже, nginx после certbot слушает только IPv4. Добавь в настройку сайта listen [::]:443 ssl и listen [::]:80, проверь настройку nginx, перезагрузи его и покажи ответ curl -6 -I https://<твой поддомен>. Выполни сам на <адрес сервера>.

В прогоне это заняло 1,1 минуты, после правки curl -6 ответил 200.

Шаг 5. Сервис приёма заявок

Сначала заведи секрет. Это длинная случайная строка в адресе: как код домофона, только вписанный в ссылку. Кто не знает ссылку, тот получает 404 («нет такой страницы»). Секрет живёт в .env на сервере под именем WEBHOOK_SECRET, как остальные ключи из урока 1.6. Задание агенту (текст курса, в прогоне секрет заводили вместе с сервисом):

Сгенерируй случайный WEBHOOK_SECRET из 32 латинских букв и цифр и допиши его в .env проекта на сервере <адрес сервера>. Значение в ответе не печатай, покажи только, что строка WEBHOOK_SECRET в .env есть.
Пункт 1 готов (https работает). Теперь пункты 2–4 из задачи про форму, сам, без вопросов:
- веб-сервис приёма заявок на 127.0.0.1:8080 (aiohttp — он уже есть в зависимостях через aiogram), nginx проксирует https://<твой поддомен>/forma/ на него; адрес /forma/<WEBHOOK_SECRET из .env>, без верного секрета — 404;
- принимаем и JSON, и обычную форму; в лог — только названия пришедших полей, без значений;
- без согласия не сохраняем; иначе уведомление владельцу в Telegram «Источник: сайт» + строка в zayavki.xlsx на Диске с колонкой «Источник» (старым строкам — «бот»); бот при своих заявках тоже пишет «бот»; ответ форме быстро, отправку на Диск — после ответа;
- служба systemd kodium-forma (пользователь bot, автозапуск);
- тесты с подменой Диска и Telegram, запусти;
- выложи, перезапусти бота и форму, проверь curl с сервера: POST JSON {"name":"Тест Форма","phone":"+79000000009","course":"Python","consent":"да"} на https://<твой поддомен>/forma/<секрет> — покажи ответ и статусы служб. Секрет в ответе не печатай.

В прогоне шаг занял 11 минут и 31 ход. Итог: обе службы active и enabled; без секрета 404; без согласия 400, заявка не сохраняется; тестовая заявка пришла владельцу с пометкой «Источник: сайт» и легла строкой в xlsx. Форме сервис отвечает сразу, а на Диск пишет уже после ответа.

Снимок Telegram владельца из прогона пути n8n: сообщение «Новая заявка (сайт, n8n)», ниже имя (скрыто), курс Python, тестовый телефон и время «10.10.2026 08:48 МСК».

Шаг 6. Подключаем Яндекс Форму

Этот шаг делаешь ты сам, в браузере. Сначала посмотри свой секрет. Проще всего командой в терминале своего компьютера:

ssh root@<адрес сервера> "grep WEBHOOK_SECRET /opt/bot-kodium/.env"

Можно и заданием агенту: «покажи мне строку WEBHOOK_SECRET из .env на сервере». Тогда секрет останется в переписке с агентом, поэтому команда лучше. Скопируй значение после знака «=». Адрес с секретом в форму вставляешь ты, агента об этом не проси.

  1. Создай форму с вопросами: имя, телефон, курс (выбор из списка: Python, Веб-разработка, Нейросети, Пока не знаю), согласие (галочка с текстом согласия).
  2. Открой вкладку «Интеграции», найди карточку «API» и в её меню выбери «Запрос JSON-RPC POST».
  3. В поле «Адрес в интернете» вставь https://<твой поддомен>/forma/<секрет>. Метод POST.
  4. В «Параметры» нажми «Добавить параметр» четыре раза. Слева имя: name, phone, course, consent. Справа переменная «Ответ на вопрос» и нужный вопрос формы. Поставь галочку «Передавать, если значение задано».
  5. Нажми «Сохранить» и отправь форму сам как посетитель.
Три кадра Яндекс Форм: 1) карточки «Почта» и «API», открытое меню «Запрос JSON-RPC POST» / «Запрос заданным методом»; 2) карточка «Запрос JSON-RPC POST»: «Адрес в интернете» https://<твой поддомен>/forma/<секрет>, «Метод» POST, «Параметры» и «Сохранить»; подпись: в параметры добавить name, phone, course, consent; 3) заполненная форма: имя, телефон (заменены), курс Python, отмеченная галочка согласия.

Кейс: честный отказ

В прогоне форму сначала подключили без параметров. Сервис ответил 400 «без согласия», в журнале стояло «Получен JSON с полями: [jsonrpc, method, params, id]». JSON-RPC это формат «конверта»: поля формы лежат внутри params, а без параметров конверт пришёл пустым. Пустую заявку без согласия сервис правильно не сохранил.

После добавления параметров данные пришли внутри params, и сервис нужно было научить их доставать. Задание прогона:

Яндекс Форма прислала заявку, сервис ответил 400 «без согласия». В логе: «Получен JSON с полями: [jsonrpc, method, params, id]» — это формат JSON-RPC, поля формы лежат внутри params (имена параметров заказчик задаёт сам: name, phone, course, consent). Значение согласия от формы может быть не «да», а текстом галочки или true — считай согласием любое непустое значение, кроме false/нет/0. Почини разбор: если пришёл JSON с ключом params — бери поля оттуда; логируй названия полей внутри params без значений. Ответ форме в формате JSON-RPC: {"jsonrpc":"2.0","result":"ok","id":<тот же id>}. Тест на такой запрос. Запусти тесты, обнови службу kodium-forma на <адрес сервера>, проверь curl-ом JSON-RPC-запросом.

Агент справился за 4 минуты. Следующая живая заявка получила ответ 200 «Заявка #5 с сайта сохранена» и дошла до Telegram и файла.

Где смотреть, что ответил сервис: в форме вкладка «Интеграции → Выполненные интеграции», запрос оттуда можно перезапустить. Как форма повторяет запросы при сбое, разберём в уроке 3.6. На сервере журнал службы:

journalctl -u kodium-forma -n 30

Если не получилось

  • certbot пишет, что не может проверить домен → DNS-запись ещё не разошлась или A-запись указывает не на тот адрес → проверь через dns.google, подожди и повтори шаг 3.
  • curl -6 отвечает 000, по IPv4 всё работает → nginx слушает только IPv4 → задание шага 4.
  • Форма получает 404 → в адресе неверный секрет или опечатка в пути /forma/ → сверь адрес в настройках формы с выводом команды grep WEBHOOK_SECRET из шага 6.
  • Форма получает 400, в журнале поля [jsonrpc, method, params, id] → в форме нет параметров или сервис не берёт поля из params → добавь параметры и дай агенту задание из кейса.
  • В уведомлении курс «Backend» или другой текст не из списка → вопрос о курсе сделан свободным полем → поменяй на выбор из списка.

Контрольный вопрос. Ты включил ufw и разрешил только 80 и 443. Через минуту ssh к серверу перестал подключаться совсем. Что пошло не так?

АСертификат Let’s Encrypt ещё не выпустился
БСервер перезагрузился сам
ВЗакончились деньги на счёте Timeweb
ГSSH не был разрешён до включения файрвола, и вахтёр закрыл эту дверь

Подсказка: Посмотри, какие правила попали в список открытых дверей.

На курсе задания проверяются автоматически. Решить первое задание бесплатно →  ·  Подобрать курс

Контрольный вопрос. Ты отправил форму, в «Выполненных интеграциях» ответ 400, а в журнале kodium-forma строка «Получен JSON с полями: [jsonrpc, method, params, id]». Что делать первым?

АУбрать проверку согласия, чтобы заявки проходили
БПроверить, что в форме добавлены параметры name, phone, course, consent, и научить сервис брать поля из params
ВПеревыпустить сертификат
ГПерезагрузить сервер

Подсказка: Где в конверте JSON-RPC лежат ответы формы?

На курсе задания проверяются автоматически. Решить первое задание бесплатно →  ·  Подобрать курс

Контрольный вопрос. С сервера curl по IPv4 к поддомену отвечает 200, а curl -6 отвечает 000. AAAA-запись верная. Где искать причину?

АВ настройках Яндекс Формы
БВ файле zayavki.xlsx
ВВ настройке nginx: он слушает только IPv4, нужно добавить listen [::]:443 ssl и [::]:80
ГВ токене бота

Подсказка: Записи DNS в порядке. Кто на сервере принимает соединения?

На курсе задания проверяются автоматически. Решить первое задание бесплатно →  ·  Подобрать курс

Задание. МИССИЯ «Дверь для формы». Обязательна одна из двух миссий, 3.3 или 3.4: та, путь которой ты выбрал в уроке 3.2. Вторая по желанию. 1. Заведи поддомен с записями A и AAAA, проверь их через dns.google. 2. Пройди с агентом шаги 3–5: nginx, сертификат, ufw, сервис приёма, служба kodium-forma. 3. Подключи свою Яндекс Форму с четырьмя параметрами и отправь заявку как посетитель. 4. Сломай и почини: отправь запрос с неверным секретом (поменяй в адресе одну букву) и заявку без галочки согласия. Убедись, что первая получила 404, а вторая 400 и не попала в файл. Верни правильный адрес. Принято, если: приложены снимок уведомления владельцу с «Источник: сайт»; снимок строки в zayavki.xlsx; снимок «Выполненных интеграций» с успешным запросом и с отказами 404 и 400; вывод ufw status и curl -6 -I с ответом 200. Секрет, адрес сервера, токены и телефоны на снимках закрыты.

Подсказка: Начинай с проверки curl с сервера, потом подключай форму. Так ты отделишь поломки сервера от поломок формы.

✅ Готово, если: программа запускается без ошибок и выводит то, что просят в задании.

На курсе задания проверяются автоматически. Решить первое задание бесплатно →  ·  Подобрать курс

Что дальше

Путь «код» работает: форма стучится в дверь, сервис проверяет и раскладывает заявку. Дальше тот же поток соберём в n8n, без сервера и без своего кода в службах.

Итог:
• поддомен с A и AAAA, nginx слушает и IPv4, и IPv6;
• ufw включается только после правила OpenSSH;
• сервис приёма слушает 127.0.0.1:8080 за nginx, адрес с секретом, без согласия ответ 400;
• Яндекс Форма шлёт JSON-RPC, поля лежат в params, адрес с секретом вводишь сам.

← Назад  ·  ↑ В начало урока  ·  ⌂ В начало курса  ·  Вперёд →

Школа Виктора Комлева