Files
portal/app
Дмитрий d242819e27 fix(смс-клиент): три блокера выката — снимок читался без пометки клиента, правка снимка и базы была без прав
Продолжение приёмки Этапа 3 по указанию владельца: «проверь замечание — так это или нет —
и посмотри всё окружение на этот класс ошибок». Замечание оказалось верным, и рядом с ним
нашлось ещё два блокера того же семейства. Ни один из трёх не виден ни одному тесту.

🔴 В-125. Джоб отправки читал снимок получателей ВНЕ пометки клиента: строки 115 и 139
стояли голыми в handle(), а обёртка открывалась только внутри цикла. Чтение снимка ленивое —
запрос уходит не тогда, когда читателя позвали, а когда забирают очередную пачку, то есть
уже за пределами чужой транзакции, а SET LOCAL живёт только до её конца.

Замер у самой базы на ОДНОЙ строке снимка: суперпользователь (так идут все наши тесты) —
1 строка, боевая роль crm_app_user без пометки — 0, она же с пометкой — 1. Без ошибки,
молча. На бою: рассылка закрывается «готово, отправлено 0»; а если в снимке есть ждущие
своего утра — статус «ждёт утра», заморозка НЕ снимается, команда добора будит рассылку
каждые 15 минут, та снова читает ноль, и деньги висят замороженными без конца.

Починка стоит в самом читателе, а не у зовущего: он оборачивает КАЖДЫЙ свой запрос сам.
Полагаться на внимательность каждого, кто его позовёт, оказалось нельзя — ровно на этом
дыра и выросла. Зовущим не мешает: под запросом человека пометка уже стоит, вложенная
транзакция ставит то же значение.

🔴 В-126 и В-127. Этап 3 научил двух помощников ПРАВИТЬ снимок и клиентскую базу —
проставлять найденный у ДаДаты регион, пояс и оператора. А обе таблицы заводились под путь
«строки только вставляют и удаляют»: прав на правку им не выдавали. Замер: UPDATE под
боевой ролью — «нет доступа к таблице», SELECT той же ролью работает.

На бою: номер, у которого пояс не был известен сразу, не получил бы его НИКОГДА, а номер
без пояса не отправляется вовсе (решение владельца В-85). Для канала «своя база» пояс не
известен ни у одного номера, пока помощник его не проставит, — то есть этот канал не
отправил бы ни одного сообщения.

Добавочная миграция (прежние прод не перезапускает), гард на существование роли, запись
схемы v9.16. Сторож прав расширен: он спрашивал базу только про то, что модуль обещал, и
обещание опять оказалось неполным — как в В-122 неделей раньше.

⚠️ В-128, не чиню, называю. В трёх докблоках записано, будто служебная роль обходит изоляцию.
ПИЛОТ.md от 07.07: на боевом кластере её не обходит НИ ОДНА роль. Значит служебное соединение
живо не обходом, а политиками srv_bypass, и перезапуск db/03_service_bypass_policies.sql при
выкате — не подстраховка, а несущая опора. Правка текстов — уровень всего приложения, не этапа.

Доказательства.
· Тест В-125 проверяет не результат (его подделать нельзя — суперпользователь всё видит), а
  ПОРЯДОК: каждый запрос к снимку обязан идти внутри той же открытой транзакции, где уже
  выставлена пометка. Уровень вложенности отличает «пометка здесь и сейчас» от «стояла раньше,
  в другой, уже закрытой». Красный до починки показал 5 чтений, все без пометки.
· Живой прогон НАСТОЯЩЕГО джоба под боевой ролью (SET ROLE crm_app_user), парно:
  с починкой — «отправлено 2 из 2, статус готово»; без починки — «отправлено 0 из 2, статус
  готово, джоб не упал». Тот самый молчаливый сбой, вживую.
· Права: живой UPDATE под боевой ролью — до миграции «нет доступа» по обеим таблицам, после
  миграции обе правки проходят. Данные пробы откатаны.
· Вырезами трижды: убрал обёртку у чтения снимка — тест краснеет; опечатка в имени роли ВНУТРИ
  гарда (та самая, что ошибки не даёт и права молча не выдаёт) — сторож краснеет; выдал права
  только одной таблице из двух — краснеет на второй. Всё возвращено.

Обход всего окружения на этот же класс: 35 фоновых помощников и 36 команд классифицированы по
тому, ставят ли они пометку клиента и каким соединением ходят. Те, что работают без пометки,
трогают только таблицы БЕЗ изоляции (портал продаж, бот, внешние балансы). Отдельно искал именно
ловушку «ленивое чтение уезжает из чужой обёртки» — в рекламном модуле и сборщике аудитории всё
внутри. Права на автономера у всех новых таблиц выданы обеим ролям, по кошельку перекосов нет.
Не проверял вглубь маршруты портала (их закрывает общая прослойка) и модули вне рекламы/СМС.

Прогоны: СМС 241/241 (11 пачек, все с первой попытки), приём лидов 17/17, фронт 1676 зелёных +
3 пропущенных (не трогал), phpstan 2 чужие давние, pint чисто. Стенд возвращён: dev-база — те же
12 клиентов и нули по модулю, пробные строки в тестовой базе откатаны.

🔴 При выкате ветки порядок прежний и обязателен: миграции → db/03_service_bypass_policies.sql →
контрольный подсчёт политик srv_bypass (должно стать на 8 больше). Права и изоляция — разные
механизмы, эта миграция того шага не заменяет.
2026-07-29 08:34:02 +03:00
..

Laravel Logo

Build Status Total Downloads Latest Stable Version License

About Laravel

Laravel is a web application framework with expressive, elegant syntax. We believe development must be an enjoyable and creative experience to be truly fulfilling. Laravel takes the pain out of development by easing common tasks used in many web projects, such as:

Laravel is accessible, powerful, and provides tools required for large, robust applications.

Learning Laravel

Laravel has the most extensive and thorough documentation and video tutorial library of all modern web application frameworks, making it a breeze to get started with the framework.

In addition, Laracasts contains thousands of video tutorials on a range of topics including Laravel, modern PHP, unit testing, and JavaScript. Boost your skills by digging into our comprehensive video library.

You can also watch bite-sized lessons with real-world projects on Laravel Learn, where you will be guided through building a Laravel application from scratch while learning PHP fundamentals.

Agentic Development

Laravel's predictable structure and conventions make it ideal for AI coding agents like Claude Code, Cursor, and GitHub Copilot. Install Laravel Boost to supercharge your AI workflow:

composer require laravel/boost --dev

php artisan boost:install

Boost provides your agent 15+ tools and skills that help agents build Laravel applications while following best practices.

Contributing

Thank you for considering contributing to the Laravel framework! The contribution guide can be found in the Laravel documentation.

Code of Conduct

In order to ensure that the Laravel community is welcoming to all, please review and abide by the Code of Conduct.

Security Vulnerabilities

If you discover a security vulnerability within Laravel, please send an e-mail to Taylor Otwell via taylor@laravel.com. All security vulnerabilities will be promptly addressed.

License

The Laravel framework is open-sourced software licensed under the MIT license.

Демо-данные (dev)

Демо-tenant создаётся DemoSeeder автоматически при composer setup / php artisan migrate --seed в окружениях local и testing (см. DatabaseSeeder — в production DemoSeeder не запускается).

Учётные данные демо-входа:

  • URL: /login
  • Email: admin@demo.local
  • Пароль: password

Что создаётся: demo-tenant (subdomain=demo, баланс 1000 ₽ / 100 лидов), admin-пользователь, 3 проекта (сайт/звонок/СМС) и ~14 демо-сделок.

Пере-сидировать демо-данные (идемпотентно — повторный запуск не создаёт дублей):

composer demo:seed

Эквивалент: php artisan db:seed --class=DemoSeeder --force.

Если при логине демо-аккаунта возвращается 422 — демо-данные не засеяны на текущей dev-БД (например, после migrate:fresh); запустите composer demo:seed.