feat(utm): propagate join focus mode

This commit is contained in:
Panfilov Konstantin committed 2026-10-17 20:46:33 +03:00
1 parent dfbb086e2b
commit 9835bb0ef8
4 files changed
+83 -11

No files matched your search

+16 -11
View File
@@ -18,8 +18,11 @@
- `rnd` — случайное значение по весам (`weights`), так делится трафик между вариантами; - `rnd` — случайное значение по весам (`weights`), так делится трафик между вариантами;
- неизвестное значение отбрасывается, отсутствующее — заменяется на `default`. - неизвестное значение отбрасывается, отсутствующее — заменяется на `default`.
Ненастроенные параметры (`utm_source`, `keyword`, `subid` и любые другие) проходят как есть. Все метки уходят дальше Ненастроенные параметры (`utm_source`, `keyword`, `subid` и любые другие) проходят как есть. Перед переходом
в адрес сайта: сайт сохраняет их на воронку и шлёт в Keitaro. Preland всегда формирует полный resolved-набор своих восьми меток: явное допустимое значение остаётся, `rnd`
разрешается по весам, отсутствующее получает `default`. Вместе с внешними рекламными параметрами этот набор
уходит в `claim`, а затем сайт переносит его в `upscale`; ни один из восьми параметров не должен пропадать только
потому, что был назначен default.
| Метка | Что задаёт | Где | | Метка | Что задаёт | Где |
|---|---|---| |---|---|---|
@@ -30,6 +33,7 @@
| `bchoice` | попап выбора банка (`a` — показать) | сайт, оплата (#44) | | `bchoice` | попап выбора банка (`a` — показать) | сайт, оплата (#44) |
| `chb` | галочки согласия на странице оплаты (`no` — не обязательны) | сайт, оплата | | `chb` | галочки согласия на странице оплаты (`no` — не обязательны) | сайт, оплата |
| `tp` | группа тарифов | сайт, cpanel «Группы тарифов (tp)» | | `tp` | группа тарифов | сайт, cpanel «Группы тарифов (tp)» |
| `focus` | первоначальная область лупы Join: `center`/`face` | сайт, cpanel после `tp` |
### Загрузка и переход на сайт ### Загрузка и переход на сайт
@@ -61,7 +65,7 @@
| `requisites.html` | реквизиты для подвала (HTML), если в `config.json` нет `requisites`. Без обоих — **заглушка «ИП Иванов Иван Иванович»** из `template.php` | | `requisites.html` | реквизиты для подвала (HTML), если в `config.json` нет `requisites`. Без обоих — **заглушка «ИП Иванов Иван Иванович»** из `template.php` |
| `images/`, `fonts/`, `favicon/`, `*.css` | статика; `style.min.css` встраивается в страницу | | `images/`, `fonts/`, `favicon/`, `*.css` | статика; `style.min.css` встраивается в страницу |
| `.htaccess` | только сроки кэширования статики (Apache) | | `.htaccess` | только сроки кэширования статики (Apache) |
| `config.json` | единственный версионируемый рабочий конфиг всех прилендов: точная проверенная конфигурация stage `zoomstage.online`; каждый deploy доставляет его на целевой хост | | `config.json` | зафиксированный Git-снимок актуального stage-конфига; runtime-файл хоста является источником истины и обычным deploy не заменяется |
### `config.json` ### `config.json`
@@ -87,10 +91,10 @@
git clone http://git.dira89kv.beget.tech:3000/anton/preland.git && cd preland git clone http://git.dira89kv.beget.tech:3000/anton/preland.git && cd preland
# дальше — загрузка на хостинг: rsync/scp по SSH или FTP-клиент # дальше — загрузка на хостинг: rsync/scp по SSH или FTP-клиент
``` ```
3. **Конфиг и реквизиты.** Рабочий `config.json` уже лежит в репозитории и обязателен к доставке на каждый 3. **Конфиг и реквизиты.** Runtime `config.json` создаётся при первичной настройке хоста и не заменяется
приленд. Его не заменяют локальным вариантом и не исключают из синхронизации. Реквизиты задаются ключом обычной синхронизацией. Менять в нём можно только ключи, явно согласованные в задаче; после такого выпуска
`requisites` в нём либо runtime-файлом `requisites.html`. 2026-10-05 конфиг пропал после заливки, актуальный stage-снимок фиксируется в Git. Реквизиты задаются ключом `requisites` либо runtime-файлом
и преленд слал загрузку на собственный домен; это больше не допускается. `requisites.html`.
4. **nginx без Apache** (если хостинг не обрабатывает `.htaccess`): 4. **nginx без Apache** (если хостинг не обрабатывает `.htaccess`):
```nginx ```nginx
server { server {
@@ -137,12 +141,13 @@
1. Домен и HTTPS — как в «Деплой на произвольный домен», шаг 1. 1. Домен и HTTPS — как в «Деплой на произвольный домен», шаг 1.
2. Секреты `<ПРЕФИКС>_FTP_HOST`, `<ПРЕФИКС>_FTP_USER`, `<ПРЕФИКС>_FTP_PASS` (Gitea → настройки репозитория → 2. Секреты `<ПРЕФИКС>_FTP_HOST`, `<ПРЕФИКС>_FTP_USER`, `<ПРЕФИКС>_FTP_PASS` (Gitea → настройки репозитория →
Actions → секреты; порт 21, FTP с TLS). В коде реквизитов нет. Actions → секреты; порт 21, FTP с TLS). В коде реквизитов нет.
3. Никаких ручных конфигов не создавать: единственный `config.json` приезжает вместе с кодом. 3. Один раз создать runtime `config.json` с нужными значениями: последующие обычные deploy его не перезапишут.
4. Запись в `matrix` — коммит в `main`, автодеплой выкатит все цели вместе с конфигом. 4. Запись в `matrix` — коммит в `main`, автодеплой выкатит код на все цели, сохраняя их конфиги.
**Что заливается.** Каждый deploy получает файлы репозитория (`git ls-files`), кроме служебных **Что заливается.** Каждый deploy получает файлы репозитория (`git ls-files`), кроме служебных
(`.gitea/`, `README.md`, `.gitignore`) и живущего на хостинге `requisites.html`. Единственный (`.gitea/`, `README.md`, `.gitignore`), а также живущих на хостинге `requisites.html` и `config.json`.
версионируемый `config.json` всегда доставляется вместе с кодом. Перед разрешённым изменением конкретного конфигурационного ключа workflow считывает текущий host-файл и
патчит только этот ключ.
На хостинге ничего не удаляется: файлы, убранные из репозитория, удалять вручную. На хостинге ничего не удаляется: файлы, убранные из репозитория, удалять вручную.
**Проверка после заливки.** На странице цели должен быть адрес загрузки на сайте, а не на самом преленде (иначе нет **Проверка после заливки.** На странице цели должен быть адрес загрузки на сайте, а не на самом преленде (иначе нет
+7
View File
@@ -57,6 +57,13 @@
"2": 1 "2": 1
}, },
"default": "1" "default": "1"
},
"focus": {
"weights": {
"center": 1,
"face": 0
},
"default": "center"
} }
} }
} }
+59
View File
@@ -0,0 +1,59 @@
<?php
/**
* Контракт Preland → сайт: собственные метки всегда разрешены до перехода,
* а внешние рекламные параметры не теряются.
*/
declare(strict_types=1);
$_GET = [
'utm_source' => 'stage-ad',
'subid' => 'click-42',
'key' => 'must-not-replace-client-key',
];
$_SERVER['HTTP_HOST'] = 'zoomstage.online';
ob_start();
require dirname(__DIR__) . '/index.php';
$html = (string) ob_get_clean();
if (!preg_match('/const redirectUrlUtm = (.+);/', $html, $match)) {
fwrite(STDERR, "FAIL: Preland не отдал redirectUrlUtm.\n");
exit(1);
}
$query = json_decode($match[1], true);
if (!is_string($query)) {
fwrite(STDERR, "FAIL: redirectUrlUtm должен быть JSON-строкой.\n");
exit(1);
}
parse_str($query, $params);
$expected = [
'indpic' => '1',
'theme' => 'dark',
'degr' => '1',
'registration' => 'after',
'bchoice' => 'a',
'chb' => 'b',
'tp' => '1',
'focus' => 'center',
'utm_source' => 'stage-ad',
'subid' => 'click-42',
];
ksort($params);
ksort($expected);
if ($params !== $expected) {
fwrite(STDERR, "FAIL: claim URL обязан нести все resolved UTM и внешние параметры.\n");
exit(1);
}
if (isset($params['key'])) {
fwrite(STDERR, "FAIL: внешний key не должен подменять одноразовый ключ загрузки.\n");
exit(1);
}
fwrite(STDOUT, "OK: preland redirects resolved UTM and external parameters\n");
+1
View File
@@ -25,6 +25,7 @@ if (!is_array($config)) {
'bchoice' => ['a' => 1, 'b' => 0, 'default' => 'a'], 'bchoice' => ['a' => 1, 'b' => 0, 'default' => 'a'],
'chb' => ['a' => 1, 'b' => 0, 'default' => 'b'], 'chb' => ['a' => 1, 'b' => 0, 'default' => 'b'],
'tp' => ['1' => 0, '2' => 1, 'default' => '1'], 'tp' => ['1' => 0, '2' => 1, 'default' => '1'],
'focus' => ['center' => 1, 'face' => 0, 'default' => 'center'],
]; ];
if (($config['host'] ?? null) !== $expected['host']) { if (($config['host'] ?? null) !== $expected['host']) {