Собираем кабинет с Claude Code

В прогоне курса агент собрал кабинет заявок за 20,6 минуты: вход, две роли, журнал, защита форм, 8 тестов из 8 зелёные. Работа хорошая. Но в папке проекта рядом с кодом лежали две чужие папки: кэш установщика пакетов на 1,8 МБ и служебная папка Microsoft. А главная дыра, как выяснилось позже, тестами не ловилась вообще. В этом уроке собираем кабинет тем же заданием и учимся смотреть на результат шире, чем «тесты зелёные».

Чему научишься в этом уроке:
• ставить агенту большое задание на веб-приложение с ролями и тестами;
• читать отчёт агента: что проверили тесты, а что нет;
• убирать из проекта мусор и закрывать его от git;
• переделывать задание под своего заказчика.

Стек: из чего собран кабинет

Стек (набор инструментов) почти тот же, что в П1–П3, добавились части для сайта. Для ориентира, а не для запоминания:

  • FastAPI: программа на сервере, которая отвечает браузеру. Как администратор в поликлинике: принимает запрос и решает, что выдать.
  • Jinja2: шаблоны страниц. Бланк с пустыми местами, куда программа подставляет заявки. Заодно сама защищает от вредного текста в полях: если кто-то впишет в имя кусок кода, шаблон покажет его как обычный текст.
  • htmx: маленькая библиотека, чтобы страница обновляла кусок (например, статус) без перезагрузки и без сложной сборки.
  • SQLite: база в одном файле, как в мониторинге П3. Для кабинета на 2–3 человека её хватает.
  • pwdlib с Argon2: превращает пароли в нечитаемые отпечатки. Подробно в следующем уроке.
  • pytest: проверки, которые агент пишет к коду. Мы просим их сразу в задании.
Почему в задании написано «passlib НЕ использовать». В старых инструкциях пароли делают через passlib. Последняя его версия вышла в октябре 2020 года, библиотека заброшена. Документация FastAPI сегодня советует pwdlib с Argon2. Агент по памяти может взять старое, поэтому говорим прямо.

Задание 1. Кабинет у себя на компьютере

Создай новую папку, например crm-kodium, открой в ней Claude Code, как в П1–П3. В П3 (урок 4.3) мы видели: агент делает большое задание целиком, если в нём перечислено всё, что нужно, и сказано, как проверить. Вот задание из прогона:

Новый проект для «Кодиума» (паттерн «Мини-CRM»): «Кабинет заявок» — веб-админка, где менеджер и владелец работают с заявками из бота и с формы сайта.
Стек: FastAPI + шаблоны Jinja2 + htmx, база SQLite (встроенный модуль), пароли — библиотека pwdlib с Argon2 (passlib НЕ использовать, он заброшен), вход — подписанная cookie (SessionMiddleware, секрет SESSION_SECRET в .env, cookie только по HTTPS, same_site=lax). Виртуальное окружение .venv, свежие версии, на Windows tzdata.
Что должно быть:
1. Роли: менеджер — список заявок (фильтр по статусу, поиск по имени/телефону), карточка, смена статуса «новая / в работе / записан / отказ», комментарий. Владелец — всё то же + пользователи (создать, отключить, сбросить пароль), выгрузка в xlsx, мини-аналитика: заявки по источникам и доля «записан». Удалять заявки — только владелец. Права проверяются на сервере в каждом обработчике, а не только скрытием кнопок.
2. Журнал: каждая смена статуса и удаление — кто, когда, что было и что стало.
3. Приём заявок от бота и формы: POST /api/zayavka с заголовком X-Ingest-Key (ключ INGEST_KEY в .env), поля name, phone, course, source, consent_at; без верного ключа — 401; без согласия — 400.
4. Защита: CSRF-токен в каждой форме; не больше 5 попыток входа в минуту с одного адреса (6-я — 429); все SQL-запросы с параметрами (никакой склейки строк).
5. Первый владелец создаётся командой `python manage.py create-owner <логин>` (пароль спрашивает в консоли, в журнал не пишет).
6. Тесты pytest: менеджер получает 403 на удаление и на страницу пользователей; 6-я попытка входа — 429; форма без CSRF-токена — отказ; попытка SQL-инъекции в поиске ничего не ломает; приём заявки без ключа — 401, без согласия — 400; журнал пишет смену статуса.
Запусти тесты и покажи полный вывод pytest -q. На сервер пока не выкладывай.

Обрати внимание на три вещи в задании. Права проверяются в каждом обработчике (части программы, которая отвечает на один адрес), а не скрытием кнопок. Секрет и ключ приёма лежат в .env. Тесты перечислены поимённо, поэтому агент не напишет «проверки» на то, что и так работает.

Что получилось в прогоне

За 20,6 минуты и 45 ходов агент собрал проект: FastAPI с шаблонами и htmx, SQLite, pwdlib с Argon2, вход через подписанную cookie, CSRF-токены, лимит попыток входа. Тесты 8 из 8, и они настоящие: в них нет заглушек, которые подменяют проверку ролей, а права проверены в каждом обработчике.

Как это понять самому, не читая весь код? Попроси агента: «Покажи для каждого теста одной строкой, что он проверяет, и есть ли в тестах подмена проверки входа или ролей». Если тест «менеджер получает 403» на самом деле подменил менеджера заглушкой, он докажет только то, что заглушка работает.

Окно терминала в папке crm-kodium: команда pytest -v, восемь строк PASSED и итог «8 passed». Справа у каждой строки подпись: менеджер получает 403 на удаление и на «Пользователи»; 6-я попытка входа 429; форма без CSRF-токена отказ; SQL-инъекция в поиске ничего не ломает; приём без ключа 401; приём без согласия 400; журнал пишет смену статуса.

Мусор в проекте

Теперь о том, чего в отчёте не было. В папке проекта оказались папка с кэшем установщика пакетов pip (1,8 МБ) и служебная папка Microsoft. Это не код, а следы работы программ на твоём компьютере. Если их не убрать, они уедут в git, а потом на сервер.

Попроси агента убрать их и настроить исключения:

В папке проекта лежат папки pip и Microsoft — это не код проекта. Удали их из проекта и добавь в .gitignore: эти папки, .venv, файл базы, кэши и .env. Покажи итоговый .gitignore и git status.

Файл .gitignore мы разбирали в уроке 1.4: это список того, что git не трогает. Проверь глазами вывод git status: в нём не должно быть .env, файла базы и чужих папок. При выкладке на сервер мы ещё раз скажем агенту не копировать их.

Чего тесты не ловят

Восемь зелёных тестов проверяют то, что в них написано. Они не проверяют, какой секрет на самом деле лежит в .env на сервере и что программа делает, если секрета нет. В прогоне именно здесь сидела главная дыра кабинета: о ней следующий урок.

Правило паттерна: зелёные тесты значат «написанное работает», а не «кабинет безопасен». Безопасность проверяешь ты сам: читаешь код и .env, входишь под каждой ролью.

Задание 1 для своего заказчика

Для своего заказчика возьми то же задание и замени места в угловых скобках проектом из прошлого урока:

Новый проект для «<заказчик>» (паттерн «Мини-CRM»): «<название кабинета>» — веб-админка, где <роли> работают с <что: заявки, заказы, клиенты> из <откуда приходят>.
Стек: FastAPI + шаблоны Jinja2 + htmx, база SQLite (встроенный модуль), пароли — библиотека pwdlib с Argon2 (passlib НЕ использовать, он заброшен), вход — подписанная cookie (SessionMiddleware, секрет SESSION_SECRET в .env, cookie только по HTTPS, same_site=lax). Виртуальное окружение .venv, свежие версии, на Windows tzdata.
Что должно быть:
1. Роли: <роль 1> — <что может>. <роль 2> — всё то же + <что ещё>. Удалять <записи> — только <старшая роль>. Права проверяются на сервере в каждом обработчике, а не только скрытием кнопок.
2. Журнал: каждая смена статуса и удаление — кто, когда, что было и что стало.
3. Приём <записей> от <источники>: POST /api/<адрес> с заголовком X-Ingest-Key (ключ INGEST_KEY в .env), поля <твои поля>, consent_at; без верного ключа — 401; без согласия — 400.
4. Защита: CSRF-токен в каждой форме; не больше 5 попыток входа в минуту с одного адреса (6-я — 429); все SQL-запросы с параметрами (никакой склейки строк).
5. Первый <старшая роль> создаётся командой `python manage.py create-owner <логин>` (пароль спрашивает в консоли, в журнал не пишет).
6. Тесты pytest: <младшая роль> получает 403 на удаление и на страницу пользователей; 6-я попытка входа — 429; форма без CSRF-токена — отказ; попытка SQL-инъекции в поиске ничего не ломает; приём без ключа — 401, без согласия — 400; журнал пишет смену статуса.
Запусти тесты и покажи полный вывод pytest -q. На сервер пока не выкладывай.

Строки, которые не меняются никогда:

  • стек и pwdlib с Argon2, passlib не использовать;
  • секрет и ключ приёма только в .env;
  • права проверяются на сервере в каждом обработчике;
  • журнал «кто, когда, было, стало»;
  • CSRF-токен, лимит 5 попыток входа в минуту, SQL с параметрами;
  • тесты на 403, 429, CSRF, SQL-инъекцию, 401 и 400, журнал;
  • «покажи полный вывод pytest -q» и «на сервер пока не выкладывай».

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

  • Тестов меньше, чем пунктов в задании → агент пропустил часть; назови пропущенные пункты и попроси добавить.
  • Агент поставил passlib или bcrypt вместо pwdlib → верни задание с цитатой «passlib НЕ использовать» и попроси показать список пакетов в requirements.
  • Тест на 403 проходит, а в нём подменена проверка ролей → попроси переписать тест без подмены: настоящий вход менеджером и запрос на удаление.
  • В git status видны .env, файл базы или папки pip/Microsoft → задание про .gitignore выше; .env в git не должен попасть никогда (урок 1.6).
  • На Windows падает работа со временем → в задании есть «на Windows tzdata», попроси поставить этот пакет.

Контрольный вопрос. Агент отчитался: «8 из 8 тестов прошли». Что это доказывает?

АЧто кабинет безопасен и можно выкладывать
БЧто на сервере правильный .env
ВЧто пароли надёжные
ГЧто работает то, что проверяют написанные тесты, не больше

Подсказка: Проверяет ли тест то, чего в нём нет?

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

Контрольный вопрос. В папке проекта после агента лежат папки pip и Microsoft. Что делаешь?

АОставляю, они нужны для работы
БУдаляю из проекта и добавляю в .gitignore вместе с .venv, базой, кэшами и .env
ВВыкладываю на сервер вместе с кодом
ГУдаляю .gitignore, чтобы не мешал

Подсказка: Это код проекта или следы программ на компьютере?

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

Контрольный вопрос. Почему в задании сказано «права проверяются на сервере в каждом обработчике, а не только скрытием кнопок»?

АТак быстрее работает сайт
БКнопки нельзя скрывать в Jinja2
ВБез кнопки адрес всё равно можно набрать руками, и сервер должен отказать
ГТак требует htmx

Подсказка: Вспомни дверь со шторкой из прошлого урока.

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

Задание. МИССИЯ «Кабинет у себя». 1. Дай агенту задание 1 из урока. Для своего заказчика возьми вариант с местами в угловых скобках из раздела «Задание 1 для своего заказчика»; строки из списка «не меняются никогда» оставь как есть. 2. Попроси показать, что проверяет каждый тест одной строкой и нет ли подмены входа или ролей. 3. Убери мусор и настрой .gitignore заданием из урока, проверь git status глазами. 4. Сломай и почини: попроси агента временно убрать проверку роли в обработчике удаления и запустить тесты. Тест «менеджер получает 403 на удаление» должен упасть. Верни проверку, тесты снова зелёные. Принято, если: приложены вывод pytest -q с числом тестов, список «тест → что проверяет», вывод git status без .env, базы и чужих папок, и снимок упавшего теста из шага 4.

Подсказка: Если в шаге 4 тест не упал, значит он проверяет что-то другое. Спроси агента, что именно.

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

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

Что дальше

Кабинет работает у тебя на компьютере. В следующем уроке разберём вход, пароли и права изнутри и найдём дыру, которую восемь тестов пропустили.

Итог:
• кабинет собран одним заданием с перечисленными тестами за 20,6 минуты;
• pwdlib с Argon2 вместо заброшенного passlib;
• мусор агента убираем, .env и база в .gitignore;
• зелёные тесты не значат «безопасно».

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

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