Scheduled Postgres backup, restore check, backup warnings in health, payment result logging

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-08 17:32:06 +03:30
parent a5feee053a
commit 7d81d193bd
11 changed files with 449 additions and 5 deletions

View File

@@ -144,6 +144,43 @@ def _ai_provider_state() -> dict:
return {'state': row['new_status'], 'since': row['created_at'].isoformat()}
def _backup_warnings() -> list[str]:
"""بکاپِ روزانه، کپیِ بیرونی و آزمونِ بازیابی؛ فقط وقتی Postgres است (SQLite یعنی توسعه‌ی محلی)."""
from datetime import datetime, timezone as dt_tz
from statuslog.backup import read_status
if settings.DEBUG or connection.vendor != 'postgresql':
return []
st = read_status()
now = datetime.now(dt_tz.utc)
def age_hours(iso: str):
return (now - datetime.fromisoformat(iso)).total_seconds() / 3600 if iso else None
out = []
if st.get('corrupt'):
return ['status.json بکاپ خوانده نشد؛ لاگ را ببینید.']
age = age_hours(st.get('last_ok_at'))
if age is None:
out.append('هیچ بکاپِ زمان‌بندی‌شده‌ای ثبت نشده (manage.py backup_db با cron اجرا نمی‌شود).')
elif age > settings.BACKUP_MAX_AGE_HOURS:
out.append(f'آخرین بکاپِ موفق {age:.0f} ساعت پیش بود (سقف {settings.BACKUP_MAX_AGE_HOURS}).')
if st.get('last_error'):
out.append(f"آخرین تلاشِ بکاپ شکست خورد: {st['last_error'][:200]}")
if (st.get('offsite') or {}).get('state') != 'ok':
out.append('کپیِ بکاپ بیرون از این ماشین نیست یا شکست خورده (BACKUP_OFFSITE_CMD).')
rc = st.get('restore_check') or {}
rc_age = age_hours(rc.get('at'))
if rc_age is None:
out.append('بازیابیِ بکاپ هنوز آزموده نشده (manage.py backup_verify).')
elif not rc.get('ok'):
out.append('آخرین آزمونِ بازیابی ناهمخوان بود.')
elif rc_age > settings.RESTORE_CHECK_MAX_AGE_DAYS * 24:
out.append(f'آخرین آزمونِ بازیابی {rc_age / 24:.0f} روز پیش بود.')
return out
def _warnings() -> list[str]:
"""
چیزهایی که خرابی نیستند ولی روی یک سرورِ عمومی خطرناک‌اند.
@@ -182,6 +219,7 @@ def _warnings() -> list[str]:
'اعتبارِ سرویسِ هوش مصنوعی (GapGPT) تمام شده: دستیار برای همه‌ی کاربرها '
'«در دسترس نیست» می‌دهد تا حساب شارژ شود.'
)
out.extend(_backup_warnings())
backend = settings.CACHES['default']['BACKEND']
if not settings.DEBUG and 'LocMem' in backend:
out.append('کش محلیِ هر پروسه است؛ محدودیتِ نرخ بینِ workerها مشترک نیست.')