fix: Add db.rollback() after SQL exceptions in status dashboard

Prevents "transaction is aborted" cascade errors when
pg_stat_statements extension is not installed or other
SQL queries fail.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Maciej Pienczyn 2026-01-31 20:30:52 +01:00
parent fca0e9d51e
commit 0530d096b0

View File

@ -238,6 +238,7 @@ def admin_status():
CompanyWebsiteAnalysis.seo_audited_at.isnot(None)
).count()
except Exception:
db.rollback() # Reset transaction after error
pass
# Cache hit ratio
@ -251,9 +252,11 @@ def admin_status():
if result and result[0] and (result[0] + result[1]) > 0:
db_metrics['cache_hit_ratio'] = round(100 * result[0] / (result[0] + result[1]), 1)
except Exception:
db.rollback() # Reset transaction after error
pass
# Slow queries (queries over 1 second in last 24h)
# Note: pg_stat_statements extension may not be installed
try:
result = db.execute(text("""
SELECT count(*) FROM pg_stat_statements
@ -261,6 +264,7 @@ def admin_status():
""")).scalar()
db_metrics['slow_queries'] = result or 0
except Exception:
db.rollback() # Reset transaction after error
db_metrics['slow_queries'] = 'N/A'
# Deadlocks
@ -271,10 +275,12 @@ def admin_status():
""")).scalar()
db_metrics['deadlocks'] = result or 0
except Exception:
db.rollback() # Reset transaction after error
pass
db_metrics['status'] = 'ok'
except Exception as e:
db.rollback() # Reset transaction after error
db_metrics['status'] = 'error'
db_metrics['error'] = str(e)[:100]
@ -302,8 +308,13 @@ def admin_status():
app_metrics['endpoints_ok'] = None
# Users statistics
app_metrics['admins'] = db.query(User).filter(User.is_admin == True).count()
app_metrics['users_with_2fa'] = db.query(User).filter(User.totp_enabled == True).count()
try:
app_metrics['admins'] = db.query(User).filter(User.is_admin == True).count()
app_metrics['users_with_2fa'] = db.query(User).filter(User.totp_enabled == True).count()
except Exception:
db.rollback()
app_metrics['admins'] = 0
app_metrics['users_with_2fa'] = 0
# Recent activity (last 24h)
yesterday = now - timedelta(days=1)
@ -313,6 +324,7 @@ def admin_status():
AuditLog.created_at >= yesterday
).count()
except Exception:
db.rollback()
app_metrics['logins_24h'] = 0
# Security alerts (last 24h)
@ -321,6 +333,7 @@ def admin_status():
SecurityAlert.created_at >= yesterday
).count()
except Exception:
db.rollback()
app_metrics['alerts_24h'] = 0
# ===== SECURITY METRICS =====
@ -333,6 +346,7 @@ def admin_status():
AuditLog.created_at >= yesterday
).count()
except Exception:
db.rollback()
security_metrics['failed_logins_24h'] = 0
# Blocked by GeoIP (24h)
@ -342,6 +356,7 @@ def admin_status():
SecurityAlert.created_at >= yesterday
).count()
except Exception:
db.rollback()
security_metrics['geoip_blocked_24h'] = 0
# Rate limit hits (24h)
@ -351,6 +366,7 @@ def admin_status():
SecurityAlert.created_at >= yesterday
).count()
except Exception:
db.rollback()
security_metrics['rate_limit_24h'] = 0
# Brute force blocked accounts
@ -359,6 +375,7 @@ def admin_status():
User.is_locked == True
).count()
except Exception:
db.rollback()
security_metrics['locked_accounts'] = 0
# Unique IPs blocked (24h)
@ -368,6 +385,7 @@ def admin_status():
).scalar()
security_metrics['unique_ips_blocked'] = result or 0
except Exception:
db.rollback()
security_metrics['unique_ips_blocked'] = 0
# ===== GUNICORN/PROCESS METRICS =====