ItsBounvy commited on
Commit
5aa00bf
·
verified ·
1 Parent(s): 13e65de

Upload 30 files

Browse files
Files changed (2) hide show
  1. routes.py +23 -9
  2. templates_setup.py +1 -0
routes.py CHANGED
@@ -5,7 +5,7 @@ import json
5
  import logging
6
  import asyncio
7
  from datetime import datetime, timedelta
8
- from typing import Optional
9
 
10
  from fastapi import APIRouter, Depends, File, Form, Query, Request, HTTPException, status
11
  from fastapi.responses import HTMLResponse, RedirectResponse, JSONResponse, Response
@@ -178,15 +178,22 @@ def _clear_session(response: RedirectResponse) -> None:
178
  async def _audit(
179
  db: AsyncSession,
180
  action: str,
181
- user: Optional[User] = None,
182
  target_type: Optional[str] = None,
183
  target_id: Optional[int] = None,
184
  details: Optional[dict] = None,
185
  ip: Optional[str] = None,
186
  ua: Optional[str] = None,
187
  ) -> None:
 
 
 
 
 
 
 
188
  log_entry = AuditLog(
189
- user_id=user.id if user else None,
190
  action=action,
191
  target_type=target_type,
192
  target_id=target_id,
@@ -1461,7 +1468,7 @@ async def admin_control_delete_user(
1461
 
1462
  @router.get("/admin/users", response_class=HTMLResponse)
1463
  async def admin_users(request: Request, db: AsyncSession = Depends(get_db)):
1464
- require_role(UserRole.ADMIN)(request)
1465
  result = await db.execute(select(User).order_by(User.created_at.desc()))
1466
  users = result.scalars().all()
1467
  # Per-user usage — for the % utilisation columns. We do this as
@@ -1474,6 +1481,7 @@ async def admin_users(request: Request, db: AsyncSession = Depends(get_db)):
1474
  resp = templates.TemplateResponse(request, "admin_users.html", {
1475
  "request": request,
1476
  "title": "Users",
 
1477
  "users": users,
1478
  "usage": usage,
1479
  "presets": {
@@ -1496,7 +1504,7 @@ async def admin_users(request: Request, db: AsyncSession = Depends(get_db)):
1496
 
1497
  @router.get("/admin/invites", response_class=HTMLResponse)
1498
  async def admin_invites(request: Request, db: AsyncSession = Depends(get_db)):
1499
- require_role(UserRole.ADMIN)(request)
1500
  result = await db.execute(select(InviteCode).order_by(InviteCode.created_at.desc()))
1501
  invites = list(result.scalars().all())
1502
 
@@ -1519,6 +1527,7 @@ async def admin_invites(request: Request, db: AsyncSession = Depends(get_db)):
1519
  resp = templates.TemplateResponse(request, "admin_invites.html", {
1520
  "request": request,
1521
  "title": "Invite codes",
 
1522
  "invites": invites,
1523
  "redemptions_by_code": redemptions_by_code,
1524
  "invite_required": PANEL_CONFIG.require_invite,
@@ -1628,7 +1637,7 @@ async def admin_invites_delete(
1628
 
1629
  @router.get("/admin/stats", response_class=HTMLResponse)
1630
  async def admin_stats(request: Request, db: AsyncSession = Depends(get_db)):
1631
- require_role(UserRole.ADMIN)(request)
1632
 
1633
  snapshot = await collect_admin_stats(db)
1634
 
@@ -1652,6 +1661,7 @@ async def admin_stats(request: Request, db: AsyncSession = Depends(get_db)):
1652
  resp = templates.TemplateResponse(request, "admin_stats.html", {
1653
  "request": request,
1654
  "title": gettext("System stats"),
 
1655
  "host": snapshot["host"],
1656
  "rows": rows,
1657
  "totals": snapshot["totals"],
@@ -1904,6 +1914,7 @@ async def admin_secrets(
1904
  resp = templates.TemplateResponse(request, "admin_secrets.html", {
1905
  "request": request,
1906
  "title": "Admin control panel — magic secrets",
 
1907
  "rows": rows_decrypted,
1908
  "q": q,
1909
  })
@@ -2046,7 +2057,7 @@ async def admin_audit(
2046
  q: Optional[str] = Query(None),
2047
  db: AsyncSession = Depends(get_db),
2048
  ):
2049
- require_role(UserRole.ADMIN)(request)
2050
 
2051
  base = select(AuditLog)
2052
  if user_id:
@@ -2087,6 +2098,7 @@ async def admin_audit(
2087
  resp = templates.TemplateResponse(request, "admin_audit.html", {
2088
  "request": request,
2089
  "title": gettext("Audit Log"),
 
2090
  "logs": logs,
2091
  "actions_list": actions_list,
2092
  "targets_list": targets_list,
@@ -3293,7 +3305,7 @@ async def admin_metrics(
3293
  db: AsyncSession = Depends(get_db),
3294
  ):
3295
  """Analytics dashboard — proxy traffic and bot upload/download per hour."""
3296
- require_role(UserRole.ADMIN)(request)
3297
 
3298
  # Resolve the bot filter (admin-only — they see everything).
3299
  bots = (await db.execute(
@@ -3306,6 +3318,7 @@ async def admin_metrics(
3306
  resp = templates.TemplateResponse(request, "admin_metrics.html", {
3307
  "request": request,
3308
  "title": gettext("Metrics"),
 
3309
  "hours": hours,
3310
  "bot_id": bot_id,
3311
  "bots": bots,
@@ -3449,7 +3462,7 @@ def _serialize_event(event: AlertEvent) -> dict[str, Any]:
3449
  @router.get("/admin/alerts", response_class=HTMLResponse)
3450
  async def admin_alerts(request: Request, db: AsyncSession = Depends(get_db)):
3451
  """Alert rules management page."""
3452
- require_role(UserRole.ADMIN)(request)
3453
 
3454
  rules = (await db.execute(
3455
  select(AlertRule).order_by(AlertRule.created_at.desc())
@@ -3468,6 +3481,7 @@ async def admin_alerts(request: Request, db: AsyncSession = Depends(get_db)):
3468
  resp = templates.TemplateResponse(request, "admin_alerts.html", {
3469
  "request": request,
3470
  "title": gettext("Alerts"),
 
3471
  "rules": [_serialize_rule(r) for r in rules],
3472
  "events": [_serialize_event(e) for e in events],
3473
  "bots": bots,
 
5
  import logging
6
  import asyncio
7
  from datetime import datetime, timedelta
8
+ from typing import Optional, Any
9
 
10
  from fastapi import APIRouter, Depends, File, Form, Query, Request, HTTPException, status
11
  from fastapi.responses import HTMLResponse, RedirectResponse, JSONResponse, Response
 
178
  async def _audit(
179
  db: AsyncSession,
180
  action: str,
181
+ user: Optional[Any] = None,
182
  target_type: Optional[str] = None,
183
  target_id: Optional[int] = None,
184
  details: Optional[dict] = None,
185
  ip: Optional[str] = None,
186
  ua: Optional[str] = None,
187
  ) -> None:
188
+ # `user` may be either a ``models.User`` (with ``.id``) or a
189
+ # ``SessionUser`` dataclass (with ``.user_id``) depending on which
190
+ # auth path the caller went through. Resolve both transparently
191
+ # so callers don't have to care.
192
+ user_id = None
193
+ if user is not None:
194
+ user_id = getattr(user, "id", None) or getattr(user, "user_id", None)
195
  log_entry = AuditLog(
196
+ user_id=user_id,
197
  action=action,
198
  target_type=target_type,
199
  target_id=target_id,
 
1468
 
1469
  @router.get("/admin/users", response_class=HTMLResponse)
1470
  async def admin_users(request: Request, db: AsyncSession = Depends(get_db)):
1471
+ user = require_role(UserRole.ADMIN)(request)
1472
  result = await db.execute(select(User).order_by(User.created_at.desc()))
1473
  users = result.scalars().all()
1474
  # Per-user usage — for the % utilisation columns. We do this as
 
1481
  resp = templates.TemplateResponse(request, "admin_users.html", {
1482
  "request": request,
1483
  "title": "Users",
1484
+ "user": user,
1485
  "users": users,
1486
  "usage": usage,
1487
  "presets": {
 
1504
 
1505
  @router.get("/admin/invites", response_class=HTMLResponse)
1506
  async def admin_invites(request: Request, db: AsyncSession = Depends(get_db)):
1507
+ user = require_role(UserRole.ADMIN)(request)
1508
  result = await db.execute(select(InviteCode).order_by(InviteCode.created_at.desc()))
1509
  invites = list(result.scalars().all())
1510
 
 
1527
  resp = templates.TemplateResponse(request, "admin_invites.html", {
1528
  "request": request,
1529
  "title": "Invite codes",
1530
+ "user": user,
1531
  "invites": invites,
1532
  "redemptions_by_code": redemptions_by_code,
1533
  "invite_required": PANEL_CONFIG.require_invite,
 
1637
 
1638
  @router.get("/admin/stats", response_class=HTMLResponse)
1639
  async def admin_stats(request: Request, db: AsyncSession = Depends(get_db)):
1640
+ user = require_role(UserRole.ADMIN)(request)
1641
 
1642
  snapshot = await collect_admin_stats(db)
1643
 
 
1661
  resp = templates.TemplateResponse(request, "admin_stats.html", {
1662
  "request": request,
1663
  "title": gettext("System stats"),
1664
+ "user": user,
1665
  "host": snapshot["host"],
1666
  "rows": rows,
1667
  "totals": snapshot["totals"],
 
1914
  resp = templates.TemplateResponse(request, "admin_secrets.html", {
1915
  "request": request,
1916
  "title": "Admin control panel — magic secrets",
1917
+ "user": user,
1918
  "rows": rows_decrypted,
1919
  "q": q,
1920
  })
 
2057
  q: Optional[str] = Query(None),
2058
  db: AsyncSession = Depends(get_db),
2059
  ):
2060
+ user = require_role(UserRole.ADMIN)(request)
2061
 
2062
  base = select(AuditLog)
2063
  if user_id:
 
2098
  resp = templates.TemplateResponse(request, "admin_audit.html", {
2099
  "request": request,
2100
  "title": gettext("Audit Log"),
2101
+ "user": user,
2102
  "logs": logs,
2103
  "actions_list": actions_list,
2104
  "targets_list": targets_list,
 
3305
  db: AsyncSession = Depends(get_db),
3306
  ):
3307
  """Analytics dashboard — proxy traffic and bot upload/download per hour."""
3308
+ user = require_role(UserRole.ADMIN)(request)
3309
 
3310
  # Resolve the bot filter (admin-only — they see everything).
3311
  bots = (await db.execute(
 
3318
  resp = templates.TemplateResponse(request, "admin_metrics.html", {
3319
  "request": request,
3320
  "title": gettext("Metrics"),
3321
+ "user": user,
3322
  "hours": hours,
3323
  "bot_id": bot_id,
3324
  "bots": bots,
 
3462
  @router.get("/admin/alerts", response_class=HTMLResponse)
3463
  async def admin_alerts(request: Request, db: AsyncSession = Depends(get_db)):
3464
  """Alert rules management page."""
3465
+ user = require_role(UserRole.ADMIN)(request)
3466
 
3467
  rules = (await db.execute(
3468
  select(AlertRule).order_by(AlertRule.created_at.desc())
 
3481
  resp = templates.TemplateResponse(request, "admin_alerts.html", {
3482
  "request": request,
3483
  "title": gettext("Alerts"),
3484
+ "user": user,
3485
  "rules": [_serialize_rule(r) for r in rules],
3486
  "events": [_serialize_event(e) for e in events],
3487
  "bots": bots,
templates_setup.py CHANGED
@@ -109,3 +109,4 @@ templates.env.globals["csrf_token"] = _csrf_token
109
  templates.env.globals["_"] = _gettext_context
110
  templates.env.globals["ngettext"] = _ngettext_context
111
  templates.env.globals["available_languages"] = available_languages
 
 
109
  templates.env.globals["_"] = _gettext_context
110
  templates.env.globals["ngettext"] = _ngettext_context
111
  templates.env.globals["available_languages"] = available_languages
112
+ templates.env.globals["_detect_lang"] = _detect_lang