Files
portal/app/.env.example
T
Дмитрий df70a78e17 merge: ветка Билайна и Мотива сведена с главной — 347 коммитов, один затык
Затык был ровно один — routes/web.php: главная добавила ручку суточного итога
сторожей с машины робота, ветка добавила приёмник статусов Билайна. Ручки
независимые, оставлены обе.

Попутно поправлено примечание в config/services.php: оно говорило «заявка подана,
срок до десяти рабочих дней», а имя `Liderra.ru` было ОДОБРЕНО обеими сетями
10.08.2026 в тот же день — проверено глазами в кабинете. Примечание, которое
врёт, хуже отсутствующего: следующая смена прочла бы его и не стала поднимать
выключатель.

Замер на сведённом коде:
- модуль СМС 575/575, 7151 проверка (опора ветки была 548 — главная принесла 27);
- фронтенд 2205 из 2210. Пять падений — ЧУЖИЕ и приехали из главной:
  KoshelyokZakladkiPoKanalam.spec.ts, экран AdWalletMoneyColumn.vue спотыкается
  об отсутствующее поле frozen_breakdown. Доказано отпечатками: и тест, и экран
  байт в байт совпадают с gitea/main, моя правка их не касалась;
- Pint и PHPStan — чисто.
2026-08-10 21:39:33 +03:00

282 lines
19 KiB
Bash
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
APP_NAME=Liderra
APP_ENV=local
APP_KEY=
APP_DEBUG=true
APP_URL=http://localhost
APP_LOCALE=ru
APP_FALLBACK_LOCALE=ru
APP_FAKER_LOCALE=ru_RU
APP_TIMEZONE=Europe/Moscow
APP_MAINTENANCE_DRIVER=file
# APP_MAINTENANCE_STORE=database
# PHP_CLI_SERVER_WORKERS=4
BCRYPT_ROUNDS=12
LOG_CHANNEL=stack
LOG_STACK=single
LOG_DEPRECATIONS_CHANNEL=null
LOG_LEVEL=debug
DB_CONNECTION=pgsql
DB_HOST=127.0.0.1
DB_PORT=5432
DB_DATABASE=liderra
DB_USERNAME=postgres
DB_PASSWORD=
# Supplier Sync (Plan 3 Task 3) — crm_supplier_worker BYPASSRLS connection.
# На production указывают на роль crm_supplier_worker (создана db/00_create_roles.sql v1.1).
# Если не заданы — fallback на DB_USERNAME / DB_PASSWORD (dev: postgres superuser, BYPASSRLS implicit).
DB_SUPPLIER_USERNAME=crm_supplier_worker
DB_SUPPLIER_PASSWORD=
# Supplier Portal credentials (Playwright login — Task 5)
SUPPLIER_LOGIN=
SUPPLIER_PASSWORD=
SUPPLIER_PORTAL_URL=https://crm.bp-gr.ru
# Supplier alerts (email через Unisender Go relay)
SUPPLIER_ALERT_EMAIL=
# Сторожа машины робота (З-0.10). Машина 51.250.1.97 раз в сутки гоняет своих
# сторожей и стучится сюда с итогом. Тревога уходит на MONITORING_ALERT_EMAIL
# (он же падает на SUPPLIER_ALERT_EMAIL, тот — на ops@liderra.ru).
# 🔴 LENA_STOROZHA_SLOVO — тайное слово подписи тела. Короче 32 знаков или
# пусто = дорога закрыта совсем. То же слово кладётся на машину в файл
# /home/ubuntu/lena-golos/nastroyki-obhoda (права 600), в хранилище его нет.
LENA_STOROZHA_SLOVO=
# Кому позволено стучать: адреса/подсети через запятую. Пусто на бою = закрыто.
LENA_STOROZHA_ADRESA=
# Сколько сторожей ждём в итоге. Прибавили сторожа на машине — прибавьте здесь.
LENA_STOROZHA_SKOLKO=3
# Сколько часов итог считается свежим (обход ходит раз в сутки).
LENA_STOROZHA_SVEZHEST_CHASOV=26
# SaaS-admin fail-closed гейт (M-1). Логины nginx basic-auth (.htpasswd-admin),
# допущенные в /api/admin/*. CSV; дефолт совпадает с прод-.htpasswd.
ADMIN_ALLOWED_USERS=admin
# ADMIN_GATE_ENFORCED=true # авто: true вне local/testing; задать явно для override
# Системный admin-id для audit-trail (FK saas_admin_audit_log). На проде crm_app_user
# не имеет прав на saas_admin_users → задать id сид-стаба. dev/test — оставить пустым.
ADMIN_AUDIT_SYSTEM_USER_ID=
# Капча самозаписи (M-2). driver=null (dev) | yandex (prod). Для yandex нужен server-key.
CAPTCHA_DRIVER=null
YANDEX_SMARTCAPTCHA_SERVER_KEY=
SESSION_DRIVER=database
SESSION_LIFETIME=120
SESSION_ENCRYPT=false
SESSION_PATH=/
SESSION_DOMAIN=null
BROADCAST_CONNECTION=log
FILESYSTEM_DISK=local
QUEUE_CONNECTION=redis
CACHE_STORE=database
# CACHE_PREFIX=
MEMCACHED_HOST=127.0.0.1
REDIS_CLIENT=predis
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379
MAIL_MAILER=log
MAIL_SCHEME=null
MAIL_HOST=127.0.0.1
MAIL_PORT=2525
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_FROM_ADDRESS="hello@example.com"
MAIL_FROM_NAME="${APP_NAME}"
SUPPORT_EMAIL=support@liderra.ru
BOT_TOURS_ENABLED=false
YANDEX_GPT_API_KEY=
YANDEX_GPT_FOLDER_ID=
AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=
AWS_DEFAULT_REGION=us-east-1
AWS_BUCKET=
AWS_USE_PATH_STYLE_ENDPOINT=false
VITE_APP_NAME="${APP_NAME}"
# Клиентский ключ Yandex SmartCaptcha (M-2). Пусто → fallback-чекбокс (dev).
# На проде — клиентский ключ ysc1_… (для виджета на странице регистрации).
VITE_YANDEX_SMARTCAPTCHA_SITEKEY=
# Автоподбор шаг2: обход антибота справочников (2ГИС/Яндекс). Ключ — в .env, не в гите.
XFETCH_API_KEY=
# Автоподбор шаг1: модель ИИ-фильтра «поставщик или площадка-агрегатор».
# ОБЯЗАТЕЛЬНО gpt-4o (сильная): слабая gpt-4o-mini наугад штампует живые
# федеральные МФО (Екапуста/Webbankir/СрочноДеньги…) как «агрегаторы» и режет
# их из подбора. Пусто → фолбэк на gpt-4o-mini (баг «режет федералов», 07.07.2026).
AITUNNEL_AGGREGATOR_MODEL=gpt-4o
# Счётчик «полного пути» — тот же, что стоит на лендинге liderra.ru.
# Без него путь клиента в Вебвизоре рвётся между лендингом и кабинетом.
METRIKA_PATH_COUNTER_ID=
# СМС-маршрутизация по операторам (реестр — config/services.php services.sms.providers).
# Песочница по умолчанию ВКЛ — живые каналы не вызываются, деньги не уходят.
SMS_SANDBOX=true
SMS_FAILOVER_TO_UNIVERSAL=false
SMS_SMSC_ENABLED=false
SMS_SMSC_PRICE_KOP=0
# Имя отправителя универсального канала — им подписываемся, когда своего имени
# у клиента для сети получателя нет. Пусто = уйдёт без имени.
SMSC_NAMING=
SMS_MTS_ENABLED=false
SMS_MTS_TOKEN=
SMS_MTS_PRICE_KOP=0
# Канал Теле2 («SMS-Таргет», target.t2.ru). Логин и пароль берутся в кабинете:
# Профиль → API-рассылки → Настроить подключение. Там же заявляется IP боевого
# сервера — без этого t2 отвечает 403 wrong_ip. Имя отправителя — согласованное.
SMS_T2_ENABLED=false
SMS_T2_LOGIN=
SMS_T2_PASSWORD=
SMS_T2_NAMING=
SMS_T2_PRICE_KOP=0
# Обратный звонок t2 о судьбе: /api/webhook/t2-delivery/{секрет}?id=&status=&parts=
# Секрет — не короче 32 знаков; пустой = приёмник ЗАКРЫТ (судьбу доберёт опрос).
# 🪤 t2 ответа не проверяет и повторов не делает: пропущенный звонок потерян.
# Адреса отправителя — через запятую, можно подсетями; пусто = не проверяем.
SMS_T2_WEBHOOK_SECRET=
SMS_T2_WEBHOOK_IPS=
# Канал Билайна (платформа прямых A2P-рассылок a2p-sms.beeline.ru, метод JSON).
# Вход двух видов, довольно любого:
# • логин с паролем от кабинета рассылок — работают сразу;
# • постоянный ключ, он надёжнее. Заводится в кабинете: Карточка пользователя →
# API токены, создать может только Суперпользователь, значение показывается
# ОДИН раз. Ключ задан → логин с паролем не передаются вовсе, иначе отказ.
# 🔑 Билайн доставляет в девять сетей, НО имя отправителя согласуется с каждым
# оператором отдельно (в кабинете: Имена отправителей). Пока имя одобрено только
# Билайном — в реестре каналов serves держим ['beeline'], иначе отказ 20230.
SMS_BEELINE_ENABLED=false
SMS_BEELINE_LOGIN=
SMS_BEELINE_PASSWORD=
SMS_BEELINE_TOKEN=
SMS_BEELINE_NAMING=
SMS_BEELINE_PRICE_KOP=0
# 🔴 Сеть Мотива. Имя отправителя заказано у Билайна сразу и на неё — галка в
# форме заявки прибита намертво, снять нельзя. Поднимать выключатель ТОЛЬКО
# после того, как кабинет Билайна покажет имя согласованным (заявка 10.08.2026,
# срок до десяти рабочих дней): отправка раньше вернёт 20230 «имя не одобрено
# оператором» — отказ окончательный, повтора нет, сообщение умрёт.
# Пока выключатель опущен, номера Мотива уходят универсальным каналом.
SMS_BEELINE_SERVES_MOTIV=false
# 6,24 ₽ без НДС по прайсу × 1,23464 (НДС 22% + надбавка тарифа 1,2%) = 7,70 ₽.
# Пакетов на Мотив у Билайна нет вовсе — дешевле не станет.
SMS_BEELINE_PRICE_MOTIV_KOP=770
# Обратный звонок Билайна о судьбе: POST /api/webhook/beeline-delivery/{секрет}
# с полями ORDID, CNRID, RESCOUNT, STATUS, FINALTIME. Секрет — не короче 32 знаков;
# пустой = приёмник ЗАКРЫТ (судьбу доберёт опрос). Сам адрес надо заявить в кабинете:
# Настройки → Дополнительно → «Приём статусов отправленных сообщений».
# 🪤 Повторяет ли платформа неудавшийся звонок — в её описании не сказано ни слова:
# считаем, что пропущенный звонок потерян.
# Адреса отправителя — через запятую, можно подсетями; пусто = не проверяем.
SMS_BEELINE_WEBHOOK_SECRET=
SMS_BEELINE_WEBHOOK_IPS=
# Реклама в Телеграме по своей базе (клиентский модуль, робот-в-браузере — у МТС нет API).
# Песочница по умолчанию ВКЛ — робот доводит только до черновика, деньги не списываются.
# TG_SANDBOX=false открывается осознанно (Сессия 6) после проверки в песочнице.
TG_SANDBOX=true
# Node-робот кабинета МТС (bots/mts-telegram-ads). Воркер живёт на Windows-машине с сессией МТС.
TG_ROBOT_NODE=node
TG_ROBOT_SCRIPT=
TG_ROBOT_CWD=
TG_ROBOT_TIMEOUT=300
# Канал робота-грузчика креативов в веб-кабинет Яндекса. Пусто → канал закрыт.
CREATIVE_ROBOT_TOKEN=
# ── Поиск клиентов: свой рендер-сервис на виртуалке (решение владельца Р126) ──
# Портал просит виртуалку отрисовать страницу 2ГИС/Яндекса и вернуть данные. Тайный ключ
# едет заголовком X-Render-Key в КАЖДОМ запросе. Пока адрес или ключ пусты — дверь закрыта:
# наружу не ходим, поиск отдаёт пусто и говорит об этом в журнал. Ошибками не сыплет.
# 🔴 Р126: только https://. По http:// ключ поехал бы открытым текстом и молча. Адрес без
# шифрования портал не принимает. Единственное исключение — разработчик на СВОЁМ компьютере:
# при APP_ENV=local (или testing) И адресе на петлю этой же машины (127.0.0.0/8, ::1,
# localhost) http:// разрешён — такой запрос компьютер физически не покидает. Тумблера
# «можно без шифра» нет намеренно: он и был бы той опечаткой, ради которой запрет заведён.
# Боевое значение сегодня: SELF_RENDER_ENDPOINT=https://51-250-1-97.sslip.io/render
SELF_RENDER_ENDPOINT=
# Тайный ключ портал↔виртуалка (тот же RENDER_KEY, что на самой виртуалке). Только здесь,
# в код и в гит НЕ попадает.
SELF_RENDER_KEY=
# Сколько страниц просим отрисовать ОДНОВРЕМЕННО (браузерная параллельность — внутри виртуалки).
SELF_RENDER_CONCURRENCY=8
# Ручка проверки живости виртуалки. Пусто → пингуем сам endpoint.
SELF_RENDER_HEALTH_URL=
# 🔴 Куда порталу ВООБЩЕ разрешено ходить с тайным ключом рендера (хосты через запятую).
# Опечатка в SELF_RENDER_ENDPOINT увела бы ключ чужому человеку молча и навсегда. Хост не
# из списка = дверь закрыта, запрос не отправлен. Пусто = закрыто всё.
# 🔴 Если эту строку не задать, берётся умолчание из config/services.php — тот же список.
# Пустым его делать НЕЛЬЗЯ: на боевом строки нет, а поиск работает — пустое умолчание
# остановило бы его в момент выката.
# 🪤 Разрешённый хост — это ещё не безопасная дорога: http:// к разрешённому хосту всё
# равно закрыт. Два запрета независимы, и снимать их надо по отдельности.
SELF_RENDER_ALLOWED_HOSTS=51.250.1.97,51-250-1-97.sslip.io,127.0.0.1,localhost
# ── Прокси автоподбора (Proxy.Market): плитка «Прокси автоподбора» в админке ──
# Логин/пароль прокси лежат на ТОЙ ЖЕ виртуалке-рендере. Портал спрашивает её ручку
# /proxy-check и предъявляет ТОТ ЖЕ тайный ключ SELF_RENDER_KEY заголовком X-Render-Key.
# 🔴 Р126, третья дверь (07.08.2026): адрес проходит ту же проверку, что SELF_RENDER_ENDPOINT —
# только https:// и только хост из SELF_RENDER_ALLOWED_HOSTS. Своего списка машин у прокси НЕТ
# намеренно: ключ один, значит и список, куда его разрешено везти, обязан быть один.
# Дверь закрыта = плитка СЕРАЯ («не проверяем, причина такая-то»), а НЕ красная: красный на
# этой плитке значит «прокси кончились» и гонит владельца их продлевать. Врать красным нельзя.
# Боевое значение сегодня: PROXY_MARKET_CHECK_URL=https://51-250-1-97.sslip.io/proxy-check
PROXY_MARKET_CHECK_URL=
# Срок действия купленных прокси — руками при продлении. Пусто/не дата = плитка серая.
PROXY_MARKET_EXPIRES_AT=
# За сколько дней до конца срока плитка желтеет.
PROXY_MARKET_AMBER_DAYS=7
# Куда ведёт кнопка «Пополнить» на плитке.
PROXY_MARKET_TOPUP_URL=https://proxy.market/
# ── Обзвон: шов «портал зовёт голосового робота» (З-0.3, решение владельца Р84) ──
# Робот живёт на отдельной машине и делит железо с боевым рендером «Поиска клиентов».
# Пока адрес или ключ пусты — дверь закрыта: портал наружу не ходит, звонков не будет,
# и он говорит об этом вслух. Ошибками не сыплет.
# БАЗОВЫЙ адрес приёмника заданий, без хвоста ручки (портал сам добавит /pozvonit и др.).
# 🔴 Р123: только https://. Тайный ключ едет заголовком в каждом запросе — по http:// он
# поехал бы открытым текстом и молча. Адрес без шифрования портал не принимает: дверь
# закрыта, звонков нет. Живой робот 51.250.1.97 слушает 443 уже сегодня.
# Единственное исключение — разработчик на СВОЁМ компьютере: при APP_ENV=local (или
# testing) И адресе на петлю этой же машины (127.0.0.0/8, ::1, localhost) http:// разрешён,
# потому что такой запрос компьютер физически не покидает. Тумблера «можно без шифра»
# нет намеренно: он и был бы той опечаткой, ради которой запрет заведён.
# Пример для своей машины: OBZVON_ENDPOINT=http://127.0.0.1:8080/lena
OBZVON_ENDPOINT=
# Тайный ключ портал↔робот. Только здесь, в код и в гит НЕ попадает.
OBZVON_KEY=
# Сколько обращений к роботу разрешено ОДНОВРЕМЕННО. Предел общий на машину (замки кэша),
# а не у каждого работника очереди свой — иначе двое дали бы двойную нагрузку на чужой
# боевой рендер. 🪤 Живые разговоры этим НЕ ограничены: место освобождается сразу после
# ответа «принял», а разговор идёт минуты.
OBZVON_CONCURRENCY=4
# Сколько секунд лишнее задание ждёт свободного места. Не дождалось → «робот занят»
# (наружу не ходили, звонка точно не было, повторить позже безопасно).
OBZVON_WAIT_SECONDS=10
# Два предела времени. Первый — дозвониться до машины; второй — весь обмен целиком,
# вместе с чтением. Второй нужен против «робот отдаёт ответ по байту бесконечно»:
# соединение установилось, а чтение висит и держит работника очереди.
OBZVON_CONNECT_TIMEOUT=5
OBZVON_TIMEOUT=15
# 🔴 Куда порталу ВООБЩЕ разрешено ходить с тайным ключом (хосты через запятую).
# Опечатка в OBZVON_ENDPOINT увела бы ключ чужому человеку молча. Хост не из списка =
# дверь закрыта. Пусто = закрыто всё. Переезд робота — осознанная правка в двух местах.
# 🪤 Разрешённый хост — это ещё не безопасная дорога: http:// к разрешённому хосту всё
# равно закрыт (Р123). Два запрета независимы, и снимать их надо по отдельности.
OBZVON_ALLOWED_HOSTS=51.250.1.97,51-250-1-97.sslip.io,127.0.0.1,localhost