# tubev — Recommendations Backlog **⚠️ Це РЕКОМЕНДАЦІЇ, не PLANS.** Кожен пункт виявлено через audit (gocc1+2+3+4 на 2026-04-30) і чекає developer-go перед виконанням. goboss періодично нагадує про unprocessed items на session start. Типи: **REFACTOR** (структурна зміна без зміни поведінки) / **CLEANUP** (видалення dead code) / **BUG** (неправильна поведінка) / **FEATURE** (нове). Деталі: [REFACTOR_RULES.md](REFACTOR_RULES.md). --- ## 🚨 HIGH severity — Security ### S1. SRI / integrity для third-party JS *(BUG/FEATURE)* **Where:** layout*.etlua усіх 14 сайтів **Files:** `ajax.googleapis.com/.../jquery.min.js`, `cdn.jsdelivr.net/...flatpickr/pikaday`, `verifycdn.agego.com/v1/verify.js`, `https://a5.g--o.info`, `https://t1.tubev.sex/static/js/{interdata.min,sync}.js` **Why:** Compromise CDN → arbitrary JS у браузері користувача. Особливо `agego.com`, `g--o.info` — third-party без вашого контролю. **What:** pin-version + `integrity="sha384-..."` для кожного CDN script ABO self-host. **Effort:** M (2-4h per site, batch'ами) **Status:** OPEN. Чекає developer-go. ### S2. Origin audit obfuscated JS *(BUG)* **Where:** 10+ файлів random-name JS, не у sync.sh **Files:** `c24c4cd9.js`, `N7Ym4F.js`, `popa.js`, `f4T4s.js`, `gD0A3.js`, `qDap9.js`, `mo.js`, `aOKM9.js`, `9qFdXQ7.js`, `gk5LT.js`, `PxqbeCb.js`, `k3KL5.js` **Sites:** 8084, 8085, 8102, 8107, 8112, 8126, 8131 **Why:** Підозріло на adblock-bypass payloads ABO malicious drop. Нема git track, нема sync.sh entry, обфусковані імена. **What:** Audit походження. Якщо ваші → перейменувати + покласти у sync.sh + git track. Якщо невідомі → видалити після перевірки що active loading не залежить. **Effort:** S (1-2h) **Status:** OPEN. Питання до developer: ваші чи невідомі? --- ## 🟡 MEDIUM severity ### M1. 8112 — два GA properties в одному gtag *(BUG)* **Where:** `backup_8112/views/modules/ga/ga.etlua` **Why:** gtag config містить одночасно `G-CQN2KFSXSG` і `G-WW50B2EGSJ` → дані пишуться на 2 property. Imовірний bug — не сумісно з legitimate analytics workflow. **What:** Підтвердити з developer який ID правильний. Видалити інший. **Effort:** XS (15 min) **Status:** OPEN. Питання до developer. ### M2. site_domain / GA ID / CDN host → config *(REFACTOR)* **Where:** layout/header `.etlua` всіх 14 сайтів **Why:** Site domain hardcoded у CSP `form-action`, `preconnect`, partner logo links. GA IDs hardcoded. CDN hosts hardcoded. Зараз — по N літералів у кожному layout. Drift-prone. **What:** Створити `views/config/site.lua` (або еквівалент Lua module) з constants: ```lua return { domain = "hdsexvideo.xxx", ga_id = "G-CQN2KFSXSG", cdn_pic = "cdn-pic.hdsexvideo.xxx", cdn_video = "...", partners = { "fulltube.xxx", "nu-bay.com", ... }, } ``` Layout uses `<%= site.domain %>`, `<%= site.ga_id %>`. **Effort:** M (4-6h) **Status:** OPEN. ### M3. CSP — meta → HTTP header *(REFACTOR)* **Where:** layout*.etlua з `` **Why:** meta-CSP не може встановити `frame-ancestors` / `report-uri`, повільніший за HTTP header. Зараз 8084 (та інші) використовують meta. **What:** Перенести у nginx config. Може треба developer-side change бо nginx config не наш scope. **Effort:** S frontend-side / depends backend **Status:** OPEN. Залежить від developer. ### M4. modules/video.etlua → shared partial *(REFACTOR)* **Where:** `views/modules/video.etlua` × 14/14 сайтів **Why:** 67-77% common lines, найвища similarity з усіх модулів. 14 окремих копій що data-loop ідентичний, але class names + tweaks per site. **What:** - Extract base partial з `<%= include 'modules/video_base' %>` - Per-site theme = CSS overrides (не template fork) - Shared params: `<%= video, css_class, lazy_load %>` **Effort:** M (4-6h) **Status:** OPEN. **Найбільший SSOT win** з усіх refactor candidates. ### M5. 8081 layout split *(REFACTOR)* **Where:** `backup_8081/views/layout.etlua` (1223 рядки monolithом) **Why:** Outlier. Median решти ~200 рядків. Усі profile_*.etlua унікальні тут — 8081 has different architecture (NL agego, premium fork). **What:** Розбити на partials аналогічно іншим 13 сайтам (header.etlua + footer.etlua + bottom_block.etlua + main.etlua). **Effort:** L (8-12h, ПІСЛЯ M4 — щоб 8081 теж пристосувався до shared video partial) **Status:** OPEN. Залежність: M4. ### M6. 8112 static/ duplicate tree *(CLEANUP/REFACTOR)* **Where:** `backup_8112/static/{js,css,img,jsplayer,player,trade-th,tmp}` ↔ `backup_8112/views/static/...` **Why:** Дві ідентичні копії (md5-confirmed). Один live, інший stale. **What:** З'ясувати який referenced (читай OpenResty config якщо доступно, або питай developer). Видалити інший. **Effort:** M (2-4h, потребує OpenResty config check) **Status:** OPEN. Питання до developer. ### M7. 8085 submodule drift *(BUG)* **Where:** `backup_8085/views/static/js/lib` **Why:** Modified submodule reference (`m` у git status). Submodule гілка drift'нула від трекованого коміту. Або локальний edit, або prod оновив submodule а tracking — ні. **What:** З'ясувати з developer: rollback або commit поточний state. **Effort:** XS (15 min) **Status:** OPEN. Питання до developer. ### M8. 8086 sync drift — 30+ legacy файлів *(CLEANUP)* **Where:** `backup_8086/views/` **Why:** Sync.sh селективно копіює ~25 файлів, але диск містить ~30+ legacy не у sync.sh: `asdadsasd.html`, `layout_bak_25-05-18.etlua`, `id_index_copy_with_cjannels.etlua`, `del___search_top.etlua`, `header_with_country_if.etlua`, `layout_copy_withads.etlua`, plus 24+ модулів. **What:** 1. Audit чи щось з цього reference'ed у живому layout/modules 2. Unreferenced → видалити 3. Sync 8086 на rsync-парадигму (Group A) — `--delete` зачистить решту **Effort:** S (1-2h) **Status:** OPEN. ### M9. 8112 untracked drift *(CLEANUP/BUG)* **Where:** `backup_8112/{rotator/,lib/,nginx.conf,nginx.conf.compiled,redirect_*.nginx}` **Why:** Untracked у git, але присутні на disk. `rotator/` має 40+ файлів `1_.txt` (i18n) + `cats.txt` symlink → `/home/nosfortube/frontend_8112/rotator/data/cats.txt`. backup mirror'ить prod app structure частково. **What:** Or: - Додати у sync.sh + git track (якщо потрібно у backup) - Виключити (`gitignore` + не sync) (якщо не потрібно) **Effort:** S (1h) **Status:** OPEN. Питання до developer. --- ## 🟢 LOW severity / cleanup ### C1. Видалити DEL_* tombstones *(CLEANUP)* **Where:** 24 файли × 6 сайтів **Files:** `DEL_channel_id.etlua`, `DEL_friend_list.etlua`, `DEL_lang.etlua`, `DEL_lang_mob.etlua` **Sites:** 8100, 8102, 8107, 8112, 8131, 8133 **Why:** Префікс DEL_ явно signals tombstone, але файли досі живуть. Шум grep-у, ризик випадкового деплою. **Effort:** XS (15 min) **Status:** OPEN. ### C2. Видалити backup-via-rename артефакти *(CLEANUP)* **Where:** ~30 файлів з пробілами / " copy" / "with ads" / "without ads" / "with titles" **Files:** Окремі категорії: - `header copy bongo link 25_10_22.etlua` × 5 сайтів (8084, 8085, 8112, 8126, 8131; з typo `bogolink` на 8112) - `id_index copy_ads_actual.etlua` × 2 (8084, 8085) - `cat_list_thumb with ads.etlua` × 10 (8112) - `* with titles.etlua` × 5 (8100) - `untitled file` × 2 (8126) - `my copy.js`, `machine copy.js`, etc. × 8 **Why:** File names з пробілами ламають shell scripts. Версії повинні бути у git. **Effort:** S (1h) **Status:** OPEN. ### C3. Видалити legacy `*_old/` dirs *(CLEANUP)* **Where:** `backup_8085/views/static/{js_old/,img_old/}` (6+25 файлів) **Why:** "_old" — explicit legacy marker. **Verification:** `grep -rn "js_old\|img_old" backup_8085/` → 0 матчів = safe to delete. **Effort:** XS (30 min) **Status:** OPEN. ### C4. Видалити legacy UA-* GA *(CLEANUP)* **Where:** `views/modules/ga/ga.etlua`, `static/js/ga.js`, `layout-orig.etlua` **Sites:** 8081, 8086, 8100, 8112, 8126 **Why:** Universal Analytics EOL 2023-07-01. UA-* IDs не shipping data, але збільшують attack surface (extra script tag) і гасять performance. **Files з UA-:** `UA-128492434-7`, `UA-109631925-*`, `UA-105546426-7` (7 унікальних) **Effort:** XS (30 min) **Status:** OPEN. ### C5. Видалити paginator_old / video_test варіанти *(CLEANUP)* **Where:** `views/modules/` **Sites:** 8084 і 8085 **Files:** `paginator_old.etlua`, `cat_list_thumb_test.etlua`, `cat_index_test.etlua`, `main_list_test.etlua`, `video_test.etlua`, `counters.min.test.js` (8133) **Why:** Tests / older versions у prod tree. **Effort:** XS (15 min) **Status:** OPEN. ### C6. Уніфікувати sync.sh на rsync *(REFACTOR)* **Where:** 7 selective sites: 8081, 8086, 8112, 8120, 8129, 8133, 8148 **Why:** 7 з 14 уже на ідентичному rsync template (74a6acb…). 7 інших роблять те саме через ad-hoc whitelists. Drift-magnet, важко synced модулі для нових сайтів. **What:** Замінити selective `cp` allowlists на 12-line rsync template. Виняток — 8148 build-step (виносити окремо в `build.sh`). **Effort:** S (1h) **Status:** OPEN. ### C7. 8148 — винести build з sync.sh *(REFACTOR)* **Where:** `backup_8148/sync.sh` (виконує `sed -i` MUTATION на prod source) **Why:** sync.sh не повинен мутувати джерело. Build step і sync — різні concerns. **What:** - `build.sh` робить ad-bundle build + md5 + sed - `sync.sh` робить тільки rsync mirror - Pipeline: `build.sh && sync.sh` — explicit ordering **Effort:** S (1h) **Status:** OPEN. ### C8. Naming convention enforcement *(FEATURE/REFACTOR)* **Where:** Pre-commit hook (per-repo) ABO server-side guard **Why:** Запобігти повторного появи pробілів у file names, " copy", DEL_*, _test*, obfuscated random names. Інакше backlog буде відновлюватися. **What:** - Pre-commit script blocks: `[ a-zA-Z0-9_./-]` чорний список (тільки лат+цифри+`_./- `) - Block `^DEL_`, `*test*` у production paths - Document у [docs/NAMING.md](NAMING.md) (TODO P4) **Effort:** S (1h script + 1h documentation) **Status:** OPEN. ### C9. Min/non-min JS policy *(REFACTOR)* **Where:** `c2.{js,min.js}`, `main.{js,min.js}`, `my.{js,min.js}`, `jquery-3.2.1.{js,min.js}` etc. **Why:** Production = тільки `.min.js`. Dev = source. Зараз обидва у репо — шумно. **What:** Pre-commit minifier ABO build-step pipeline. Source у `src/`, minified у `static/js/`. **Effort:** M (3-5h, build pipeline) **Status:** OPEN. Потенційно потребує developer-side build infrastructure. ### C10. CSS file naming standardization *(REFACTOR)* **Where:** 8084 (no `views/css/` folder), 8086 (`styles.etlua` instead of `css_.etlua`), 8148 (no folder) **Why:** 11/14 use `css_.etlua` pattern → решта 3 не вписуються. **What:** Привести 8084/8086/8148 до `css_.etlua` convention. Якщо вони мають окрему причину (8148 — sprite-driven, не split per page) — задокументувати у [SITES.md](SITES.md) виняток. **Effort:** S (1-2h) **Status:** OPEN. ### C11. Cross-site partner logos через config *(REFACTOR)* **Where:** `header*.etlua`, layout footer-блоки 7 сайтів **Sites:** 8084, 8085, 8086, 8100, 8102, 8126, 8131 **Why:** Hardlinks на партнерські сайти (`fulltube.xxx`, `hdsexvideo.xxx`, `nu-bay.com`, `gaypornhd.xxx`, `xxxtube1.com`, `fullhdxxx.com`, `maturesexmovies.xxx`) — кожен layout має свої literals. **What:** Винести у `views/config/partners.lua` як array. Layout iterates. **Effort:** S (1-2h, після M2 site.lua) **Status:** OPEN. Залежність: M2. --- ## 🛠 INFRASTRUCTURE / Workflow ### W1. Hook conflict — Claude editing у `/home/nosfortube/` *(BUG/FEATURE)* ✅ DONE 2026-05-02 **Resolution:** Granular policy реалізована у `guard-readonly.sh` + `guard-bash.sh`. ALLOW `frontend_/` (digits-only) + `orest/`. DENY lang variants, frontend_core, .git, system paths. Backup попередніх hooks: `.bak.2026-05-02`. Test matrix: 19/19 readonly + 18/19 bash (1 pre-existing sed-i regex limitation). ### W2. backup_/ scope — 14 із 94 сайтів *(FEATURE)* **Where:** `/home/w4/backup_*/` **Problem:** git-tracking покриває 14 сайтів, інші 80 без recovery. Можливі причини: історичний субсет / pilot / залишки старого етапу. **Options:** - Зберегти 14 (subset для критичних сайтів) - Поширити на всі 94 (масовий setup `sync.sh` + initial commits) - Замінити на централізований моніторинг + adminskий git **Effort:** L якщо поширити (ініт repos + sync.sh per site + перші commits). **Status:** OPEN. Залежить від наскільки git-history важливий vs adminove ownership. ### W3. PSI automonitoring *(FEATURE)* **Where:** Future infra **Problem:** Поточний PSI check — manual через pagespeed.web.dev, не systematic. Регресії можуть бути непомічені до next ad-revenue падіння. **What:** PSI API + cron → daily snapshot + history db + alert при падінні. Або Lighthouse-CI на t1.* після deploy. **Effort:** M-L (setup ~6-12h) **Status:** DEFERRED (in-mind, не пріоритет — поки adspyglass dashboard як signal). --- ## 📚 DOC items ### D1. ad-bundle source files location **Where:** [ADS.md § Source files](ADS.md#source-files) **What:** Знайти де живуть 5 source JS-файлів (`ad-config.js`, `ad-core.js`, `ad-mute.js`, `vast-preroll.js`, `ad-bootstrap.js`) — `frontend_/views/static/js/` чи shared. Записати у ADS.md. **Effort:** XS (15 min, `find /home/nosfortube/ -name 'ad-config.js'`) **Status:** OPEN — потрібен hook resolution (W1) щоб дослідити. ### D2. Mirror swap mechanism **Where:** [ADS.md § Open questions](ADS.md#open-questions-for-developer) Q3 **What:** Зрозуміти як Адмін заміняє banned mirror — через ad-config.js mass replace, runtime config, чи інше. Записати у ADS.md. **Effort:** S (питання до developer + 1 example) **Status:** OPEN. ### D3. CDN provider **Where:** [DEPLOY.md § Open questions](DEPLOY.md#open-questions) **What:** Який CDN на prod (Cloudflare / BunnyCDN / Fastly / custom). Per-site або unified. **Effort:** XS (питання до developer) **Status:** OPEN. ### D4. Template ad-zone pattern **Where:** [ADS.md § Template integration](ADS.md#template-integration) **What:** 1 sample template fragment з ad-zone для розуміння placement pattern. **Effort:** XS (показати 5-10 рядків з template) **Status:** OPEN. --- ## Reminders cadence goboss перевіряє цей файл **на початку кожної session** і нагадує developer-у: - Скільки items OPEN (по severity) - Що було додано з минулої session - Найдешевші items (XS-S) — пропонувати спершу - Найпріоритетніші (HIGH) — окремо помічати Items з статусом `DONE`, `WONTFIX`, `DEFERRED` переходять у footer цього файлу (history), щоб не плутатися з активним backlog. --- ## DONE / WONTFIX / DEFERRED *(порожньо — поки нічого не виконано)* --- *Last update: 2026-05-01 (W1-W3 workflow + D1-D4 doc items added).* *Initial backlog: 2026-04-30 (gocc1+2+3+4 audits).* ## D-canonical-archive — Canonical points to `/v/` instead of archive URL pattern (13 sites) **Зона:** Backend (admin/dev) — потребує kor reboot. **Symptom:** На archive routes (`/v/arch/`, `/v-cache/`, `/v/stored/`, etc. per CSV new-url-pattern) canonical link генерується як `/v//...` (standard) замість archive URL. Robots `noindex,follow` правильно set, але canonical не reflects archive. **Root cause:** `views/id_index.etlua` line ~90 hardcodes `/v/` у canonical generation. Frontend не має access до runtime URL (`ngx.var.uri` not accessible у etlua context, `mysettings.cur_uri` not set). **Reference (8161 has partial setup):** - `var.archive_video_dir = "/v-arch/"` - `var.canonical_without_archive = 1` — explicit strip → canonical = standard `/v/` (BY DESIGN для 8161) - `app.lua` line 2408-2409 використовує `archive_video_dir` для URL formation **Fix per site (13 sites — крім 8161 яке by design strip):** ```lua -- lib//var.lua var.archive_video_dir = "" -- e.g. "/v/arch/" для 8159 var.canonical_without_archive = 0 -- canonical KEEPS archive prefix var.nofollow_on_archive = 1 -- if not already ``` Per CSV `new-url-pattern` column: | Port | new-url-pattern | |------|-----------------| | 8159 | /v/arch/ | | 8163 | /v-cache/ | | 8158 | /v/stored/ | | 8151 | /v/old/ | | 8160 | /v/storage/ | | 8153 | /v/archive/ | | 8154 | /v/unlisted/ | | 8155 | /v/shelved/ | | 8156 | /v/disabled/ | | 8157 | /v/bin/ | | 8162 | /v-storage/ | | 8150 | /video/old/ | | 8152 | /v/parked/ | Plus `subrouter.lua` route registration якщо missing — verify each site handles archive route. **Verify post-deploy:** ```bash curl -sL "https://www.//" | grep -oE 'rel="canonical" href="[^"]+"' # Expected: href="https://www.//" (canonical=archive URL) ``` **Frontend template** (`id_index.etlua`) можливо потребує adjustment коли backend exposes `mysettings.cur_archive_path` чи similar — depending on app.lua implementation. Reported 2026-05-05 by user audit на 8159 → bug confirmed на ВСІХ 13 sites with archive routing setup. ## D-canonical-archive-regression — REGRESSION у нових `getLinks()` (~20 sites) **Update 2026-05-05:** Audit deeper показав що це **regression**, не intended config. **Reference (correct behavior — pre-batch sites):** - broxxx.com (8133), pornv.xxx (8120): same `var.canonical_without_archive=1`, але archive route → `getLinks()` повертає `cur_canonical=""` → layout skip emit → **no canonical on archive** ✓ - Це matching intent variable name "canonical_without_archive" = "canonical видається без archive context, тобто на archive не emit-иться". **Bug (regression — newer sites):** - 8161, 8159, 8163, 8158, 8151, 8152, 8153, 8154, 8155, 8156, 8157, 8158, 8160, 8162, 8140, 8141, 8142, 8145, 8148, 8149: same config `=1`, але archive route → `cur_canonical=` → canonical EMITS до /v/. - Це over-engineered — backend strips archive prefix замість returning empty. **Scope:** ~20 sites with newer code version. Different `getLinks()` implementations у `lib//content.lua`. **Fix (backend dev task):** - Identify які content.lua versions мають regression. - Patch `getLinks()` to return `cur_canonical=""` when `canonical_without_archive=1` AND archive route. - Match broxxx/pornv behavior (старий правильний reference). - kor reboot per site. **Verify:** після fix на archive URL — `` має бути absent (як у broxxx/pornv). Reported 2026-05-05 by user audit. Frontend template OK — issue у `getLinks()` core return value.