feat(analytics): PWA detection via cookie + expanded bot filter
Some checks are pending
NordaBiz Tests / Unit & Integration Tests (push) Waiting to run
NordaBiz Tests / E2E Tests (Playwright) (push) Blocked by required conditions
NordaBiz Tests / Smoke Tests (Production) (push) Blocked by required conditions
NordaBiz Tests / Send Failure Notification (push) Blocked by required conditions

1. PWA: frontend sets pwa_mode=1 cookie when in standalone mode,
   backend reads it and stores is_pwa=True in user_sessions.
   Migration 063 adds is_pwa column.

2. Bot filter: added 13 new patterns (GoogleAssociationService,
   Censys, Palo Alto, Netcraft, fasthttp, Apple WebKit prefetch,
   etc.) + flag empty/bare "Mozilla/5.0" UA as bot. This eliminates
   ~800 false sessions from analytics.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Maciej Pienczyn 2026-03-18 09:10:46 +01:00
parent 233cbb667d
commit d38066e64f
4 changed files with 26 additions and 2 deletions

View File

@ -4180,6 +4180,9 @@ class UserSession(Base):
# Bot detection # Bot detection
is_bot = Column(Boolean, default=False) is_bot = Column(Boolean, default=False)
# PWA detection
is_pwa = Column(Boolean, default=False)
# UTM Parameters (kampanie marketingowe) # UTM Parameters (kampanie marketingowe)
utm_source = Column(String(255), nullable=True) # google, facebook, newsletter utm_source = Column(String(255), nullable=True) # google, facebook, newsletter
utm_medium = Column(String(255), nullable=True) # cpc, email, social, organic utm_medium = Column(String(255), nullable=True) # cpc, email, social, organic

View File

@ -0,0 +1,4 @@
-- Migration 063: Add is_pwa column to user_sessions
-- Tracks whether the session originated from the PWA (installed app)
ALTER TABLE user_sessions ADD COLUMN IF NOT EXISTS is_pwa BOOLEAN DEFAULT FALSE;

View File

@ -2371,6 +2371,11 @@
} }
} }
// PWA detection — set cookie for backend analytics
if (window.matchMedia('(display-mode: standalone)').matches || window.navigator.standalone) {
document.cookie = 'pwa_mode=1; path=/; max-age=86400; SameSite=Lax';
}
// PWA Smart Banner logic // PWA Smart Banner logic
(function() { (function() {
var banner = document.getElementById('pwaSmartBanner'); var banner = document.getElementById('pwaSmartBanner');

View File

@ -54,9 +54,17 @@ def get_or_create_analytics_session():
browser_version = ua.browser.version_string browser_version = ua.browser.version_string
os_name = ua.os.family os_name = ua.os.family
os_version = ua.os.version_string os_version = ua.os.version_string
is_bot = ua.is_bot or any(p in ua_string.lower() for p in ua_lower = ua_string.lower()
is_bot = ua.is_bot or any(p in ua_lower for p in
['curl/', 'python-requests', 'axios/', 'wget/', 'scrapy', ['curl/', 'python-requests', 'axios/', 'wget/', 'scrapy',
'werkzeug', 'leakix', 'nuclei', 'masscan', 'zgrab', 'httpx']) 'werkzeug', 'leakix', 'nuclei', 'masscan', 'zgrab', 'httpx',
'googleassociationservice', 'censysinspect', 'paloaltonetworks',
'cortex', 'netcraft', 'fasthttp', 'cms-checker',
'wp-safe-scanner', 'notebooklm', 'ruby/', 'skypeuri',
'com.apple.webkit', 'networkingextension'])
# Flag empty or bare Mozilla/5.0 user agents as bots
if not ua_string.strip() or ua_string.strip() == 'Mozilla/5.0':
is_bot = True
except Exception: except Exception:
device_type = 'desktop' device_type = 'desktop'
browser = 'Unknown' browser = 'Unknown'
@ -77,6 +85,10 @@ def get_or_create_analytics_session():
os_version=os_version[:20] if os_version else None, os_version=os_version[:20] if os_version else None,
is_bot=is_bot is_bot=is_bot
) )
# PWA detection from cookie
is_pwa = request.cookies.get('pwa_mode') == '1'
user_session.is_pwa = is_pwa
db.add(user_session) db.add(user_session)
db.commit() db.commit()
db.refresh(user_session) db.refresh(user_session)