mbuzz 0.7.0__py3-none-any.whl → 0.7.3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- mbuzz/__init__.py +1 -1
- mbuzz/api.py +1 -1
- mbuzz/middleware/flask.py +65 -0
- mbuzz/utils/__init__.py +2 -1
- mbuzz/utils/fingerprint.py +12 -0
- {mbuzz-0.7.0.dist-info → mbuzz-0.7.3.dist-info}/METADATA +1 -1
- {mbuzz-0.7.0.dist-info → mbuzz-0.7.3.dist-info}/RECORD +8 -7
- {mbuzz-0.7.0.dist-info → mbuzz-0.7.3.dist-info}/WHEEL +0 -0
mbuzz/__init__.py
CHANGED
mbuzz/api.py
CHANGED
mbuzz/middleware/flask.py
CHANGED
|
@@ -1,14 +1,46 @@
|
|
|
1
1
|
"""Flask middleware for mbuzz tracking."""
|
|
2
2
|
# NOTE: Session cookie removed in 0.7.0 - server handles session resolution
|
|
3
3
|
|
|
4
|
+
import threading
|
|
5
|
+
import uuid
|
|
6
|
+
from datetime import datetime, timezone
|
|
7
|
+
from typing import Optional
|
|
8
|
+
|
|
4
9
|
from flask import Flask, request, g, Response
|
|
5
10
|
|
|
11
|
+
from ..api import post
|
|
6
12
|
from ..config import config
|
|
7
13
|
from ..context import RequestContext, set_context, clear_context
|
|
8
14
|
from ..cookies import VISITOR_COOKIE, VISITOR_MAX_AGE
|
|
15
|
+
from ..utils.fingerprint import device_fingerprint
|
|
9
16
|
from ..utils.identifier import generate_id
|
|
10
17
|
|
|
11
18
|
|
|
19
|
+
def should_create_session() -> bool:
|
|
20
|
+
"""Determine whether this request is a real page navigation.
|
|
21
|
+
|
|
22
|
+
Primary signal: Sec-Fetch-* headers (modern browsers, unforgeable).
|
|
23
|
+
Fallback: blacklist known sub-request framework headers (old browsers/bots).
|
|
24
|
+
"""
|
|
25
|
+
mode = request.headers.get("Sec-Fetch-Mode")
|
|
26
|
+
dest = request.headers.get("Sec-Fetch-Dest")
|
|
27
|
+
|
|
28
|
+
if mode:
|
|
29
|
+
return (
|
|
30
|
+
mode == "navigate"
|
|
31
|
+
and dest == "document"
|
|
32
|
+
and not request.headers.get("Sec-Purpose")
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
# Fallback for old browsers / bots: blacklist known sub-requests
|
|
36
|
+
return (
|
|
37
|
+
not request.headers.get("Turbo-Frame")
|
|
38
|
+
and not request.headers.get("HX-Request")
|
|
39
|
+
and not request.headers.get("X-Up-Version")
|
|
40
|
+
and request.headers.get("X-Requested-With") != "XMLHttpRequest"
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
|
|
12
44
|
def init_app(app: Flask) -> None:
|
|
13
45
|
"""Initialize mbuzz tracking for Flask app."""
|
|
14
46
|
|
|
@@ -24,6 +56,11 @@ def init_app(app: Flask) -> None:
|
|
|
24
56
|
_set_request_context(visitor_id, ip, user_agent)
|
|
25
57
|
_store_in_g(visitor_id)
|
|
26
58
|
|
|
59
|
+
if should_create_session():
|
|
60
|
+
_create_session_async(
|
|
61
|
+
visitor_id, request.url, request.referrer, ip, user_agent
|
|
62
|
+
)
|
|
63
|
+
|
|
27
64
|
@app.after_request
|
|
28
65
|
def after_request(response: Response) -> Response:
|
|
29
66
|
if not hasattr(g, "mbuzz_visitor_id"):
|
|
@@ -83,6 +120,34 @@ def _store_in_g(visitor_id: str) -> None:
|
|
|
83
120
|
g.mbuzz_is_new_visitor = VISITOR_COOKIE not in request.cookies
|
|
84
121
|
|
|
85
122
|
|
|
123
|
+
def _create_session_async(
|
|
124
|
+
visitor_id: str,
|
|
125
|
+
url: str,
|
|
126
|
+
referrer: Optional[str],
|
|
127
|
+
ip: str,
|
|
128
|
+
user_agent: str,
|
|
129
|
+
) -> None:
|
|
130
|
+
"""Fire-and-forget session creation via background thread.
|
|
131
|
+
|
|
132
|
+
All data is captured before the thread starts — no request-object
|
|
133
|
+
access inside the thread (it would be invalid after the response).
|
|
134
|
+
"""
|
|
135
|
+
payload = {
|
|
136
|
+
"session": {
|
|
137
|
+
"visitor_id": visitor_id,
|
|
138
|
+
"session_id": str(uuid.uuid4()),
|
|
139
|
+
"url": url,
|
|
140
|
+
"referrer": referrer,
|
|
141
|
+
"device_fingerprint": device_fingerprint(ip, user_agent),
|
|
142
|
+
"started_at": datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ"),
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
threading.Thread(
|
|
147
|
+
target=post, args=("/sessions", payload), daemon=True
|
|
148
|
+
).start()
|
|
149
|
+
|
|
150
|
+
|
|
86
151
|
def _set_cookies(response: Response) -> None:
|
|
87
152
|
"""Set visitor cookie on response."""
|
|
88
153
|
secure = request.is_secure
|
mbuzz/utils/__init__.py
CHANGED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"""Device fingerprint generation — matches server-side SHA256(ip|user_agent)[0:32]."""
|
|
2
|
+
|
|
3
|
+
import hashlib
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def device_fingerprint(ip: str, user_agent: str) -> str:
|
|
7
|
+
"""Compute a device fingerprint from IP and User-Agent.
|
|
8
|
+
|
|
9
|
+
Produces a 32-char hex string identical to the server-side computation
|
|
10
|
+
and the Ruby/Node SDKs.
|
|
11
|
+
"""
|
|
12
|
+
return hashlib.sha256(f"{ip}|{user_agent}".encode()).hexdigest()[:32]
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
mbuzz/__init__.py,sha256=
|
|
2
|
-
mbuzz/api.py,sha256=
|
|
1
|
+
mbuzz/__init__.py,sha256=OQDjNSQTB6Yfc66NRpZPN8cWjWcVJopDysoB8tb3cbU,2150
|
|
2
|
+
mbuzz/api.py,sha256=bOLjQRgv9PbfGvNxYMtdyiBQvlLFHsHcVgZ8_lEWEHU,2107
|
|
3
3
|
mbuzz/config.py,sha256=u6v1WnMTgiqyDXHszlXw5hyNENgYRy7-z-IWKuYp0-o,2399
|
|
4
4
|
mbuzz/context.py,sha256=tcvtKaAbHzH8dHGkKviNgXF31IKgintqo0lhb6uU4dU,1308
|
|
5
5
|
mbuzz/cookies.py,sha256=pDcvSq0ulzKrFLf5UK408s9Lv2pVzLANVaF_-P7iG3c,194
|
|
@@ -8,9 +8,10 @@ mbuzz/client/conversion.py,sha256=mUkxU8FgbmvvXnliTskawnDzbwn1SxutZ5E4rFkNt5o,31
|
|
|
8
8
|
mbuzz/client/identify.py,sha256=duyd-OWdBo4w7-7Nv5X-GeG2IpMI-1G_kYwnDXup0FE,859
|
|
9
9
|
mbuzz/client/track.py,sha256=NReHqXWx24cPhIlc7UBQjj9QVdnXfrJsFzS5UmOUMtA,4354
|
|
10
10
|
mbuzz/middleware/__init__.py,sha256=gIjwTArToaQNB2NC0iPE_RcmzuHjH7-7jjd7_Dyq4Pw,37
|
|
11
|
-
mbuzz/middleware/flask.py,sha256=
|
|
12
|
-
mbuzz/utils/__init__.py,sha256
|
|
11
|
+
mbuzz/middleware/flask.py,sha256=gkvwtgfLasTKq64lUvK_9I2PeOR9oBRbrWpQZbqnaAk,4656
|
|
12
|
+
mbuzz/utils/__init__.py,sha256=tq5wgr4Cy-PFl2tvpN5VYHzPd3M-e5qcxnv-McoTopI,169
|
|
13
|
+
mbuzz/utils/fingerprint.py,sha256=T2plrP9n70LkoCvGbBCh7nxZRfdQpCd_CdZBseBfzCg,410
|
|
13
14
|
mbuzz/utils/identifier.py,sha256=iAYmd4he2RTtTNlmszb6KQmD9McZbSMgZZdkMuoGOCE,174
|
|
14
|
-
mbuzz-0.7.
|
|
15
|
-
mbuzz-0.7.
|
|
16
|
-
mbuzz-0.7.
|
|
15
|
+
mbuzz-0.7.3.dist-info/METADATA,sha256=Q0dHPns1Lk73_ziPwsknxzQMIkJFAYnqRo5drKkCD_0,1849
|
|
16
|
+
mbuzz-0.7.3.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
17
|
+
mbuzz-0.7.3.dist-info/RECORD,,
|
|
File without changes
|