Jethro85 commited on
Commit ·
b2a55ac
1
Parent(s): e573ae1
log to supabase
Browse files- app/routes.py +112 -11
app/routes.py
CHANGED
|
@@ -7,6 +7,33 @@ from app.training.mock_trainer import MockTrainer
|
|
| 7 |
from app.training.privacy_calculator import PrivacyCalculator
|
| 8 |
from flask_cors import cross_origin
|
| 9 |
import os
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 10 |
|
| 11 |
# Try to import RealTrainer, fallback to MockTrainer if dependencies aren't available
|
| 12 |
try:
|
|
@@ -289,15 +316,70 @@ def _ensure_vid_cookie(resp=None):
|
|
| 289 |
return vid, resp
|
| 290 |
return vid, resp
|
| 291 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 292 |
@main.route("/api/whoami", methods=["GET"])
|
| 293 |
@cross_origin()
|
| 294 |
def whoami():
|
| 295 |
ua = request.headers.get("User-Agent", "")[:200]
|
| 296 |
dnt = request.headers.get("DNT") == "1"
|
| 297 |
gpc = request.headers.get("Sec-GPC") == "1"
|
| 298 |
-
|
| 299 |
-
payload = {"vid":
|
| 300 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 301 |
|
| 302 |
@main.route('/api/track', methods=['POST', 'OPTIONS'])
|
| 303 |
@cross_origin()
|
|
@@ -311,15 +393,21 @@ def track_event():
|
|
| 311 |
gpc = request.headers.get("Sec-GPC") == "1"
|
| 312 |
ua = request.headers.get("User-Agent", "")[:200]
|
| 313 |
ref = request.headers.get("Referer", "")[:200]
|
| 314 |
-
vid, resp = _ensure_vid_cookie()
|
| 315 |
|
| 316 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 317 |
user = evt.get("user") if isinstance(evt.get("user"), dict) else {}
|
| 318 |
-
allowed_user = {k: user[k] for k in ["id", "role", "org", "plan"]
|
|
|
|
| 319 |
|
| 320 |
evt.update({
|
| 321 |
"server_time": datetime.utcnow().isoformat() + "Z",
|
| 322 |
-
"visitor_id":
|
|
|
|
| 323 |
"client_ip_truncated": _client_ip(),
|
| 324 |
"user_agent": ua,
|
| 325 |
"referer": ref,
|
|
@@ -328,11 +416,24 @@ def track_event():
|
|
| 328 |
"user": allowed_user
|
| 329 |
})
|
| 330 |
|
| 331 |
-
|
| 332 |
-
|
| 333 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 334 |
|
| 335 |
-
return jsonify({'ok': True})
|
| 336 |
except Exception as e:
|
| 337 |
return jsonify({'ok': False, 'error': str(e)}), 400
|
|
|
|
|
|
|
|
|
|
| 338 |
# ===== End Analytics =====
|
|
|
|
| 7 |
from app.training.privacy_calculator import PrivacyCalculator
|
| 8 |
from flask_cors import cross_origin
|
| 9 |
import os
|
| 10 |
+
import requests
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
SUPABASE_URL = os.getenv("SUPABASE_URL", "")
|
| 14 |
+
SUPABASE_SERVICE_KEY = os.getenv("SUPABASE_SERVICE_KEY", "")
|
| 15 |
+
|
| 16 |
+
def supabase_insert_event(row: dict) -> None:
|
| 17 |
+
"""Insert one event row into Supabase (best-effort)."""
|
| 18 |
+
if not SUPABASE_URL or not SUPABASE_SERVICE_KEY:
|
| 19 |
+
# No secrets set; silently skip to avoid breaking your app
|
| 20 |
+
return
|
| 21 |
+
|
| 22 |
+
r = requests.post(
|
| 23 |
+
f"{SUPABASE_URL}/rest/v1/events",
|
| 24 |
+
headers={
|
| 25 |
+
"apikey": SUPABASE_SERVICE_KEY,
|
| 26 |
+
"Authorization": f"Bearer {SUPABASE_SERVICE_KEY}",
|
| 27 |
+
"Content-Type": "application/json",
|
| 28 |
+
"Prefer": "return=minimal",
|
| 29 |
+
},
|
| 30 |
+
json=row,
|
| 31 |
+
timeout=3,
|
| 32 |
+
)
|
| 33 |
+
# optional: raise if you want strictness
|
| 34 |
+
if r.status_code >= 300:
|
| 35 |
+
print("Supabase insert failed:", r.status_code, r.text[:300])
|
| 36 |
+
|
| 37 |
|
| 38 |
# Try to import RealTrainer, fallback to MockTrainer if dependencies aren't available
|
| 39 |
try:
|
|
|
|
| 316 |
return vid, resp
|
| 317 |
return vid, resp
|
| 318 |
|
| 319 |
+
# @main.route("/api/whoami", methods=["GET"])
|
| 320 |
+
# @cross_origin()
|
| 321 |
+
# def whoami():
|
| 322 |
+
# ua = request.headers.get("User-Agent", "")[:200]
|
| 323 |
+
# dnt = request.headers.get("DNT") == "1"
|
| 324 |
+
# gpc = request.headers.get("Sec-GPC") == "1"
|
| 325 |
+
# vid, resp = _ensure_vid_cookie()
|
| 326 |
+
# payload = {"vid": vid, "user_agent": ua, "dnt": dnt, "gpc": gpc}
|
| 327 |
+
# return jsonify(payload)
|
| 328 |
+
|
| 329 |
+
# @main.route('/api/track', methods=['POST', 'OPTIONS'])
|
| 330 |
+
# @cross_origin()
|
| 331 |
+
# def track_event():
|
| 332 |
+
# if request.method == 'OPTIONS':
|
| 333 |
+
# return jsonify({'status': 'ok'})
|
| 334 |
+
# try:
|
| 335 |
+
# evt = request.get_json(force=True, silent=True) or {}
|
| 336 |
+
#
|
| 337 |
+
# dnt = request.headers.get("DNT") == "1"
|
| 338 |
+
# gpc = request.headers.get("Sec-GPC") == "1"
|
| 339 |
+
# ua = request.headers.get("User-Agent", "")[:200]
|
| 340 |
+
# ref = request.headers.get("Referer", "")[:200]
|
| 341 |
+
# vid, resp = _ensure_vid_cookie()
|
| 342 |
+
#
|
| 343 |
+
# # Allow only specific user fields if provided
|
| 344 |
+
# user = evt.get("user") if isinstance(evt.get("user"), dict) else {}
|
| 345 |
+
# allowed_user = {k: user[k] for k in ["id", "role", "org", "plan"] if k in user and isinstance(user[k], (str, int))}
|
| 346 |
+
#
|
| 347 |
+
# evt.update({
|
| 348 |
+
# "server_time": datetime.utcnow().isoformat() + "Z",
|
| 349 |
+
# "visitor_id": vid,
|
| 350 |
+
# "client_ip_truncated": _client_ip(),
|
| 351 |
+
# "user_agent": ua,
|
| 352 |
+
# "referer": ref,
|
| 353 |
+
# "dnt": dnt,
|
| 354 |
+
# "gpc": gpc,
|
| 355 |
+
# "user": allowed_user
|
| 356 |
+
# })
|
| 357 |
+
#
|
| 358 |
+
# logfile = os.path.join(current_app.root_path, 'analytics.log.jsonl')
|
| 359 |
+
# with open(logfile, 'a', encoding='utf-8') as f:
|
| 360 |
+
# f.write(json.dumps(evt, ensure_ascii=False) + '\n')
|
| 361 |
+
#
|
| 362 |
+
# return jsonify({'ok': True})
|
| 363 |
+
# except Exception as e:
|
| 364 |
+
# return jsonify({'ok': False, 'error': str(e)}), 400
|
| 365 |
+
|
| 366 |
+
|
| 367 |
@main.route("/api/whoami", methods=["GET"])
|
| 368 |
@cross_origin()
|
| 369 |
def whoami():
|
| 370 |
ua = request.headers.get("User-Agent", "")[:200]
|
| 371 |
dnt = request.headers.get("DNT") == "1"
|
| 372 |
gpc = request.headers.get("Sec-GPC") == "1"
|
| 373 |
+
|
| 374 |
+
payload = {"vid": request.cookies.get(COOKIE_NAME) or uuid.uuid4().hex,
|
| 375 |
+
"user_agent": ua, "dnt": dnt, "gpc": gpc}
|
| 376 |
+
|
| 377 |
+
resp = make_response(jsonify(payload))
|
| 378 |
+
if not request.cookies.get(COOKIE_NAME):
|
| 379 |
+
resp.set_cookie(COOKIE_NAME, payload["vid"], max_age=60*60*24*365*2,
|
| 380 |
+
httponly=True, secure=True, samesite="Lax", path="/")
|
| 381 |
+
return resp
|
| 382 |
+
|
| 383 |
|
| 384 |
@main.route('/api/track', methods=['POST', 'OPTIONS'])
|
| 385 |
@cross_origin()
|
|
|
|
| 393 |
gpc = request.headers.get("Sec-GPC") == "1"
|
| 394 |
ua = request.headers.get("User-Agent", "")[:200]
|
| 395 |
ref = request.headers.get("Referer", "")[:200]
|
|
|
|
| 396 |
|
| 397 |
+
resp = make_response(jsonify({'ok': True}))
|
| 398 |
+
cookie_vid, resp = _ensure_vid_cookie(resp)
|
| 399 |
+
|
| 400 |
+
visitor_id = evt.get("vid") or cookie_vid
|
| 401 |
+
session_id = evt.get("sessionId")
|
| 402 |
+
|
| 403 |
user = evt.get("user") if isinstance(evt.get("user"), dict) else {}
|
| 404 |
+
allowed_user = {k: user[k] for k in ["id", "role", "org", "plan"]
|
| 405 |
+
if k in user and isinstance(user[k], (str, int))}
|
| 406 |
|
| 407 |
evt.update({
|
| 408 |
"server_time": datetime.utcnow().isoformat() + "Z",
|
| 409 |
+
"visitor_id": visitor_id,
|
| 410 |
+
"session_id": session_id,
|
| 411 |
"client_ip_truncated": _client_ip(),
|
| 412 |
"user_agent": ua,
|
| 413 |
"referer": ref,
|
|
|
|
| 416 |
"user": allowed_user
|
| 417 |
})
|
| 418 |
|
| 419 |
+
row = {
|
| 420 |
+
"visitor_id": str(visitor_id),
|
| 421 |
+
"session_id": str(session_id) if session_id else None,
|
| 422 |
+
"event_type": str(evt.get("eventType", "unknown")),
|
| 423 |
+
"payload": evt,
|
| 424 |
+
"user_agent": ua,
|
| 425 |
+
"referer": ref,
|
| 426 |
+
"client_ip_truncated": _client_ip(),
|
| 427 |
+
"dnt": dnt,
|
| 428 |
+
"gpc": gpc
|
| 429 |
+
}
|
| 430 |
+
supabase_insert_event(row)
|
| 431 |
+
|
| 432 |
+
return resp
|
| 433 |
|
|
|
|
| 434 |
except Exception as e:
|
| 435 |
return jsonify({'ok': False, 'error': str(e)}), 400
|
| 436 |
+
|
| 437 |
+
|
| 438 |
+
|
| 439 |
# ===== End Analytics =====
|