← всі звіти · index.md

type: subproject parent: interactive-banners name: Interactive Banners — Fluvir slug: banner-fluvir status: live readiness_pct: 90 brand: Fluvir (противірусний) formats: [300x250, 300x600] campaign_status: active in DV360 created: 2026-04 last_updated: 2026-05-05

Interactive Banners — Fluvir

Перший live банер у DV360. v2 (300x250+300x600) запущено після фіксу tracking-gap 2026-04-30. Реальні дані: 6.9% engaged / 75.5% passive у форматі 300x600.

Meta

Status: Live в DV360. v2 після фіксу 2026-04-30. Перший проект де реалізовано engaged/passive/interrupt-класифікацію.

KPIs (Fluvir 300x600, 2026-04-30):

Інфра

Версії

Версія DV360 ID Формати Метод tracking Статус
v1 xbid.716177674 300x250 GET-pixel LIVE, працює
v2 xbid.721925137 300x250 + 300x600 GET-pixel (після фіксу) LIVE з 2026-04-30

CDN bundles

Tracking gap fix (2026-04-30) — ключовий інцидент

Симптом: 32 events у BQ vs 31,185 impressions у DV360 за 29.04 → gap 99.9%.

Корінь: новий v2 банер використовував navigator.sendBeacon(Blob, application/json) + fetch(POST json). Обидва падають у SafeFrame/sandbox-iframe з CSP connect-src 'self'.

Чому старий банер працював: він використовував new Image().src = ENDPOINT?... (GET-pixel) — проходить через CSP img-src (ширша) і не потребує CORS preflight.

Фікс (клієнт) — index.html:

function send(name, extra) {
  var data = { sid:ctx.sid, name:name, product:ctx.product, size:ctx.size, ver:ctx.ver,
               cid:ctx.cid, crid:ctx.crid, site:ctx.site, device:ctx.device, _t: Date.now() };
  if (extra) {
    if (extra.step    != null) data.step    = extra.step;
    if (extra.dur     != null) data.dur     = extra.dur;
    if (extra.payload != null) data.payload = (typeof extra.payload === 'string') ? extra.payload : JSON.stringify(extra.payload);
  }
  var qs = [];
  for (var k in data) {
    if (data[k] == null) continue;
    qs.push(encodeURIComponent(k) + '=' + encodeURIComponent(data[k]));
  }
  try { new Image().src = ENDPOINT + '/pixel?' + qs.join('&'); } catch(_) {}
}

Фікс (Cloud Function) — track:

exports.track = async (req, res) => {
  res.set('Access-Control-Allow-Origin', '*');
  res.set('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
  if (req.method === 'GET' || req.path === '/pixel') {
    data = req.query || {};
  } else if (req.method === 'POST') {
    data = (typeof req.body === 'string') ? JSON.parse(req.body) : (req.body || {});
  }
  // ... insert у BQ ... повернути 1×1 GIF якщо GET
};

Деплой:

gcloud functions deploy track --gen2 --runtime=nodejs20 \
  --region=europe-central2 --trigger-http --allow-unauthenticated \
  --project=banner-analytics-fluvir --entry-point=track

Monitoring (systemd timers)

Timer Запуск Коли Що робить
fluvir-v2-check-4h.timer one-shot 2026-04-30 15:44 UTC ✅ FIRED BQ-запит за 4h, recap у Telegram
fluvir-v2-check-morning.timer one-shot 2026-05-01 06:00 UTC ✅ FIRED BQ-запит за 24h, recap у Telegram

Скрипт: /usr/local/bin/fluvir-v2-check.sh <hours_window> [<label>]

💡 Обидва one-shot вже відстріляли. Вирішити: вимкнути cleanup чи залишити як історія.

Backlog (specifically for Fluvir)

Cross-references