TafadzwaTaps commited on
Commit
0d670df
Β·
1 Parent(s): 6653d94

fix: latest deploymnent

Browse files
Files changed (3) hide show
  1. auth.py +12 -2
  2. db.py +189 -0
  3. tasks.py +6 -0
auth.py CHANGED
@@ -21,20 +21,30 @@ ALGORITHM = "HS256"
21
  TOKEN_TTL_MINUTES = 60 * 24 # 24 hours β€” long enough to avoid friction
22
 
23
 
24
- def create_access_token(user_id: str) -> str:
25
  """
26
  Create a signed JWT for the given user ID.
27
 
 
 
 
 
28
  The token contains:
29
  sub β€” user UUID (primary claim, used by get_current_user)
30
  iat β€” issued-at timestamp
31
  exp β€” expiry timestamp
32
  """
 
 
 
 
 
 
33
  now = datetime.now(timezone.utc)
34
  expire = now + timedelta(minutes=TOKEN_TTL_MINUTES)
35
 
36
  payload = {
37
- "sub": str(user_id),
38
  "iat": now,
39
  "exp": expire,
40
  }
 
21
  TOKEN_TTL_MINUTES = 60 * 24 # 24 hours β€” long enough to avoid friction
22
 
23
 
24
+ def create_access_token(user_id) -> str:
25
  """
26
  Create a signed JWT for the given user ID.
27
 
28
+ Accepts either:
29
+ - a plain string user UUID (new style: create_access_token(user_id))
30
+ - a dict with a "sub" key (old style: create_access_token({"sub": user_id}))
31
+
32
  The token contains:
33
  sub β€” user UUID (primary claim, used by get_current_user)
34
  iat β€” issued-at timestamp
35
  exp β€” expiry timestamp
36
  """
37
+ # Support both calling conventions without breaking either
38
+ if isinstance(user_id, dict):
39
+ sub = str(user_id.get("sub", ""))
40
+ else:
41
+ sub = str(user_id)
42
+
43
  now = datetime.now(timezone.utc)
44
  expire = now + timedelta(minutes=TOKEN_TTL_MINUTES)
45
 
46
  payload = {
47
+ "sub": sub,
48
  "iat": now,
49
  "exp": expire,
50
  }
db.py CHANGED
@@ -259,3 +259,192 @@ def get_scan_task(task_id: str, user_id: str) -> dict | None:
259
  except Exception as e:
260
  logger.error(f"get_scan_task({task_id}): {e}")
261
  return None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
259
  except Exception as e:
260
  logger.error(f"get_scan_task({task_id}): {e}")
261
  return None
262
+
263
+
264
+ # ══════════════════════════════════════════════════════════════════════════════
265
+ # BACKWARDS-COMPATIBILITY LAYER
266
+ # app.py was written against the old DB module which had different function
267
+ # names and additional helpers. All aliases and stubs live here so neither
268
+ # app.py nor the new db.py needs to change structure.
269
+ # ══════════════════════════════════════════════════════════════════════════════
270
+
271
+ # ── Name aliases (old name β†’ new function) ────────────────────────────────────
272
+ def fetch_user_by_id(user_id: str) -> dict | None:
273
+ return get_user_by_id(user_id)
274
+
275
+ def fetch_user_by_email(email: str) -> dict | None:
276
+ return get_user_by_email(email)
277
+
278
+ def user_email_exists(email: str) -> bool:
279
+ return email_exists(email)
280
+
281
+ def insert_user(data: dict) -> bool:
282
+ """Insert a user row from a raw dict (old-style call)."""
283
+ try:
284
+ # Prefer explicit fields; fall back gracefully if columns differ
285
+ _db.table("users").insert(data).execute()
286
+ return True
287
+ except Exception as e:
288
+ logger.error(f"insert_user: {e}")
289
+ return False
290
+
291
+ def fetch_scan_task(task_id: str, user_id: str | None = None) -> dict | None:
292
+ """Fetch a scan task. user_id is optional for backwards compat."""
293
+ try:
294
+ q = _db.table("scan_tasks").select("*").eq("id", task_id)
295
+ if user_id:
296
+ q = q.eq("user_id", user_id)
297
+ return _one(q.limit(1).execute())
298
+ except Exception as e:
299
+ logger.error(f"fetch_scan_task({task_id}): {e}")
300
+ return None
301
+
302
+ def insert_scan_task(data: dict) -> bool:
303
+ """Insert a scan task row from a raw dict (old-style call)."""
304
+ try:
305
+ _db.table("scan_tasks").insert(data).execute()
306
+ return True
307
+ except Exception as e:
308
+ logger.error(f"insert_scan_task: {e}")
309
+ return False
310
+
311
+ # ── Org helpers ───────────────────────────────────────────────────────────────
312
+
313
+ def insert_org(data: dict) -> bool:
314
+ """Insert an organisation row. No-op if organisations table doesn't exist."""
315
+ try:
316
+ _db.table("organisations").insert(data).execute()
317
+ return True
318
+ except Exception as e:
319
+ logger.warning(f"insert_org (non-fatal): {e}")
320
+ return False # non-fatal β€” org_id still stored on user row
321
+
322
+ def fetch_org_by_id(org_id: str) -> dict | None:
323
+ try:
324
+ return _one(_db.table("organisations").select("*").eq("id", org_id).limit(1).execute())
325
+ except Exception as e:
326
+ logger.warning(f"fetch_org_by_id({org_id}): {e}")
327
+ return None
328
+
329
+ def fetch_org_members(org_id: str) -> list:
330
+ try:
331
+ res = _db.table("users").select("id, email, is_pro, created_at").eq("org_id", org_id).execute()
332
+ return res.data or []
333
+ except Exception as e:
334
+ logger.error(f"fetch_org_members({org_id}): {e}")
335
+ return []
336
+
337
+ # ── Scan history helpers ──────────────────────────────────────────────────────
338
+
339
+ def insert_scan_history(data: dict) -> bool:
340
+ """Persist a scan result into the scans table using old field names."""
341
+ try:
342
+ # Map old field names to new schema
343
+ row = {
344
+ "user_id": data.get("user_id"),
345
+ "source": data.get("input_text", "")[:120] or "code_paste",
346
+ "risk_level": data.get("risk", "LOW"),
347
+ "total_secrets": data.get("findings_count", 0),
348
+ "result_json": {
349
+ "explanation": data.get("explanation", ""),
350
+ "fixes": data.get("fixes", []),
351
+ "score": data.get("score", 0),
352
+ "findings_count": data.get("findings_count", 0),
353
+ },
354
+ }
355
+ # Include id if provided
356
+ if data.get("id"):
357
+ row["id"] = data["id"]
358
+ _db.table("scans").insert(row).execute()
359
+ return True
360
+ except Exception as e:
361
+ logger.error(f"insert_scan_history: {e}")
362
+ return False
363
+
364
+ def fetch_scan_history(user_id: str, limit: int = 20) -> list:
365
+ return list_scans(user_id, limit=limit)
366
+
367
+ # ── Usage tracking (best-effort β€” graceful if table absent) ──────────────────
368
+
369
+ def fetch_usage_today(user_id: str, today: str) -> dict | None:
370
+ """Fetch today's usage record. Returns None if table not present."""
371
+ try:
372
+ return _one(
373
+ _db.table("usage_tracking")
374
+ .select("*")
375
+ .eq("user_id", user_id)
376
+ .eq("date", today)
377
+ .limit(1)
378
+ .execute()
379
+ )
380
+ except Exception:
381
+ return None # table may not exist β€” caller handles None
382
+
383
+ def upsert_usage(user_id: str, org_id: str, today: str, count: int) -> None:
384
+ """Update daily usage counter. Best-effort β€” never raises."""
385
+ try:
386
+ existing = fetch_usage_today(user_id, today)
387
+ if existing:
388
+ _db.table("usage_tracking").update({"request_count": count}).eq("user_id", user_id).eq("date", today).execute()
389
+ else:
390
+ _db.table("usage_tracking").insert({"user_id": user_id, "org_id": org_id, "date": today, "request_count": count}).execute()
391
+ except Exception as e:
392
+ logger.debug(f"upsert_usage (non-fatal): {e}")
393
+
394
+ def fetch_usage_history(user_id: str, limit: int = 30) -> list:
395
+ try:
396
+ res = (
397
+ _db.table("usage_tracking")
398
+ .select("date, request_count")
399
+ .eq("user_id", user_id)
400
+ .order("date", desc=True)
401
+ .limit(limit)
402
+ .execute()
403
+ )
404
+ return res.data or []
405
+ except Exception:
406
+ return []
407
+
408
+ # ── Dashboard aggregation ─────────────────────────────────────────────────────
409
+
410
+ def fetch_dashboard_data(user_id: str, org_id: str | None, plan: str) -> dict:
411
+ """Return usage series, history, and team for the dashboard endpoint."""
412
+ usage = fetch_usage_history(user_id, limit=30)
413
+ history = fetch_scan_history(user_id, limit=10)
414
+ team = fetch_org_members(org_id) if org_id else []
415
+ return {"usage": usage, "history": history, "team": team}
416
+
417
+ # ── CVE cache (in-memory β€” no DB table needed) ────────────────────────────────
418
+
419
+ _cve_cache: dict = {}
420
+
421
+ def fetch_cve_cache(query: str) -> dict | None:
422
+ return _cve_cache.get(query.lower())
423
+
424
+ def store_cve_cache(query: str, data: dict) -> None:
425
+ # Keep cache bounded β€” evict oldest entry when over 200 items
426
+ if len(_cve_cache) >= 200:
427
+ oldest = next(iter(_cve_cache))
428
+ del _cve_cache[oldest]
429
+ _cve_cache[query.lower()] = data
430
+
431
+ # ── Audit log (best-effort β€” graceful if table absent) ───────────────────────
432
+
433
+ def write_audit_log(user_id: str, action: str, **kwargs) -> None:
434
+ """Write an audit log entry. Completely non-fatal β€” never raises."""
435
+ try:
436
+ row = {
437
+ "user_id": user_id,
438
+ "action": action,
439
+ "created_at": __import__("datetime").datetime.utcnow().isoformat(),
440
+ }
441
+ row.update({k: str(v)[:500] if v else None for k, v in kwargs.items()})
442
+ _db.table("audit_logs").insert(row).execute()
443
+ except Exception as e:
444
+ logger.debug(f"write_audit_log (non-fatal): {e}")
445
+
446
+ # ── Cache invalidation stub ───────────────────────────────────────────────────
447
+
448
+ def cache_invalidate(key: str) -> None:
449
+ """No-op stub β€” in-memory caches invalidate themselves via TTL."""
450
+ pass
tasks.py CHANGED
@@ -89,3 +89,9 @@ def run_repo_scan(task_id: str, repo_url: str, user_id: str, is_pro: bool) -> No
89
  finally:
90
  if clone_dir:
91
  shutil.rmtree(clone_dir, ignore_errors=True)
 
 
 
 
 
 
 
89
  finally:
90
  if clone_dir:
91
  shutil.rmtree(clone_dir, ignore_errors=True)
92
+
93
+
94
+ # ── Backwards-compatibility alias ─────────────────────────────────────────────
95
+ # app.py imports `run_scan` (old name). The function was renamed to
96
+ # run_repo_scan but the alias keeps the import working without touching app.py.
97
+ run_scan = run_repo_scan