Claude/add auto backup ds6iu - #3139
Conversation
- גיבוי אוטומטי ל-Google Drive עם OAuth redirect flow מהוובאפ - גיבוי אוטומטי לדיסק (/var/data/repos/backups) עם ניקוי retention - Scheduler thread ברקע שסורק כל 5 דקות ומריץ גיבויים מתוזמנים - שחזור אסינכרוני עם progress bar ו-batching (מנות של 20) - תיקון אינדקס כפול ב-bookmarks (file_bookmarks_user_file_idx) - UI חדש בהגדרות: סקשנים ל-Drive, דיסק, ותדירות גיבוי https://claude.ai/code/session_019psqKqV6MhkHQRkqt8Szz5
…leanup - Race condition ב-restore_progress: snapshot כל השדות + pop בתוך אותו lock - Disk backup: עדכון last_backup_at רק בהצלחה (ok=True) - דליפת זיכרון: cleanup entries ישנים מעל שעה ב-_active_restores https://claude.ai/code/session_019psqKqV6MhkHQRkqt8Szz5
מעביר את בדיקת user_id לפני ה-pop מ-_active_restores כדי שמשתמש לא-מורשה לא ימחק entry של משתמש אחר. https://claude.ai/code/session_019psqKqV6MhkHQRkqt8Szz5
ה-before_request handler הגביל גודל רק ל-restore הסינכרוני. עכשיו מכסה גם את restore_backup_async שדרכו עובר כל ה-traffic. https://claude.ai/code/session_019psqKqV6MhkHQRkqt8Szz5
- drive_auth: כל ערכי error ב-redirects עוברים _url_encode למניעת URL שבור - backup_api: תוצאת restore לא נמחקת מיד אלא מסומנת fetched_at, ונמחקת רק אחרי 5 דקות grace — כך שאם ה-response אבד ברשת הלקוח יכול לקרוא שוב בלי לקבל 404 https://claude.ai/code/session_019psqKqV6MhkHQRkqt8Szz5
_cleanup_stale_restores היה מוחק entries שעבר שעה מאז יצירתם, בלי לבדוק אם הם עדיין בסטטוס running. עכשיו entries שרצים לא נמחקים ע"י age check — רק entries שסיימו (done/error). https://claude.ai/code/session_019psqKqV6MhkHQRkqt8Szz5
1. backup_api — העברת _active_restores מ-dict בזיכרון ל-collection restore_jobs ב-MongoDB. פותר: - High: state לא אבד ב-restart או בין workers - Low: stuck running entries נמחקים ע"י TTL index (שעה) 2. backup_scheduler — _scan_and_run משתמש ב-find_one_and_update לתפיסה אטומית של משתמשים. רק scheduler אחד מריץ גיבוי לכל משתמש, גם עם N gunicorn workers. https://claude.ai/code/session_019psqKqV6MhkHQRkqt8Szz5
אם uid או schedule_key חסרים אחרי ה-claim האטומי, מחזיר את schedule_next_at לערך המקורי כדי למנוע תקיעה ב-2099. https://claude.ai/code/session_019psqKqV6MhkHQRkqt8Szz5
כשגיבוי נכשל, schedule_next_at נדחה ב-SCAN_INTERVAL קדימה (5 דקות) במקום להחזיר את הערך המקורי שעדיין $lte now — מה שגרם ל-while True לתפוס את אותו משתמש שוב מיד (לולאה אינסופית). אותו תיקון ב-_reset_drive_schedule ו-_reset_disk_schedule. https://claude.ai/code/session_019psqKqV6MhkHQRkqt8Szz5
…handling
באג 1 (High): כיבוי schedule ל-Drive ("off") לא עצר את ה-scheduler
כי save_drive_prefs עושה merge ושדות legacy כמו schedule: "daily" נשארים.
תיקון defense-in-depth ב-3 שכבות:
- $unset שדות legacy בכיבוי (drive_backup_api.py)
- $ne "off" + $ne None בשאילתת הסריקה (backup_scheduler.py)
- early return ב-_extract_schedule_key כש-schedule_key=="off"
באג 2 (Low): _complete_restore_job חסר error handling.
אם הקריאה נכשלת ב-success path, היא נופלת ל-except ומדווחת
שחזור מוצלח כנכשל. תיקון:
- try/except ב-_complete_restore_job
- הפרדת תוצאת השחזור מהעדכון ל-DB
https://claude.ai/code/session_019psqKqV6MhkHQRkqt8Szz5
…sert, null guard)
1. drive_auth.py: _settings_redirect return type -> Response (לא str)
2. backup_api.py: בדיקת result.get("ok") is False לפני סימון "done"
כדי לא לדווח שחזור כושל כהצלחה
3. drive_backup_api.py: הסרת upsert=True ב-set_disk_schedule
כדי לא ליצור מסמך user חלקי
4. backup_scheduler.py: הוספת $ne None לשאילתת disk schedule_next_at
בהתאמה לתיקון שנעשה בשאילתת Drive
https://claude.ai/code/session_019psqKqV6MhkHQRkqt8Szz5
1. backup_scheduler.py: _extract_schedule_key — val.get("key") or val.get("value")
גורם ל-short-circuit כש-key הוא string לא-ריק אבל לא תקין,
ו-value לעולם לא נבדק. הוחלף בלולאה על שני השדות בנפרד.
2. settings.html: לולאת polling של restore ללא timeout —
אם ה-job תקוע ב-running, הדפדפן סובב עד שעה.
הוסף guard של 10 דקות (300 סיבובים × 2 שניות).
https://claude.ai/code/session_019psqKqV6MhkHQRkqt8Szz5
1. backup_scheduler.py: trigger_drive_backup_now ו-trigger_disk_backup_now לא עדכנו last_backup_at — ה-UI הציג תאריך ישן אחרי "גבה עכשיו". הוסף עדכון $set אחרי הצלחה. 2. drive_backup_api.py: save_drive_prefs עושה read-modify-write שיכול לדרוס sentinel של ה-scheduler. הוחלף ב-$set ישיר על שדות ספציפיים (dot notation), כמו שכבר עושים ב-set_disk_schedule. $unset ל-legacy fields שולב באותו update_one. https://claude.ai/code/session_019psqKqV6MhkHQRkqt8Szz5
_cleanup_disk_backups השתמש בלוגיקת OR — גיבוי נמחק אם עבר max *או* אם עבר retention. כך משתמש שלא גיבה 30+ יום איבד את כל הגיבויים הקיימים (חוץ מהאחרון). שונה ל: ה-N הכי חדשים (max) נשמרים תמיד. מחיקה חלה רק על גיבויים מעבר ל-max שגם עברו את ה-retention. https://claude.ai/code/session_019psqKqV6MhkHQRkqt8Szz5
…erwrite drive_callback השתמש ב-save_drive_prefs (read-modify-write) לשמירת drive_email, מה שיכול לדרוס את ה-sentinel של ה-scheduler. הוחלף ב-$set ישיר על drive_prefs.drive_email — אותו pattern שכבר תוקן ב-set_drive_schedule. https://claude.ai/code/session_019psqKqV6MhkHQRkqt8Szz5
1. drive_auth.py: drive_callback השתמש ב-@_require_auth שמחזיר JSON 401 — בדפדפן (redirect מ-Google) המשתמש רואה JSON גולמי. הוחלף בבדיקת session ידנית עם redirect לדף הגדרות + הודעת שגיאה. 2. backup_api.py: _complete_restore_job איפס progress ל-0 בשגיאה, מה שגורם לקפיצה אחורה בפרוגרס בר (65% → 0%). בשגיאה, progress לא נדרס — נשמר הערך האחרון. https://claude.ai/code/session_019psqKqV6MhkHQRkqt8Szz5
1. get_disk_backup_info: total_size חישב רק את 20 הראשונים אבל count החזיר את כולם — גודל מטעה ב-UI. עכשיו total_size סוכם את כל הקבצים. 2. sentinel orphan recovery: אם ה-process קרס בין claim לעדכון, ה-sentinel "2099-01-01" נשאר לנצח והגיבוי תקוע. הוסף _recover_orphaned_sentinels שרץ בתחילת כל סריקה — מאפס sentinels ל-retry_next_at. הוצא הערך הקשיח לקבוע _SENTINEL_VALUE. https://claude.ai/code/session_019psqKqV6MhkHQRkqt8Szz5
1. sentinel: הוחלף מערך קבוע "2099" ל-now + 30 דקות (SENTINEL_TTL).
- אם ה-backup מסתיים בזמן — הערך מוחלף כרגיל
- אם ה-process קרס — אחרי 30 דקות הערך פוקע ($lte now)
ונתפס מחדש טבעית
- הוסר _recover_orphaned_sentinels שאיפס גם backups פעילים
של workers אחרים
2. trigger_drive_backup_now: הוסר עדכון כפול של last_backup_at.
perform_scheduled_backup כבר מעדכן דרך save_drive_prefs —
עדכון שני ב-$set גרם ל-race condition.
https://claude.ai/code/session_019psqKqV6MhkHQRkqt8Szz5
_extract_schedule_key בscheduler היה כפילות של extract_schedule_key מ-handlers/drive/utils.py עם סדר שדות שונה ותמיכה חלקית (חסר scheduleKey, name). עכשיו משתמש בפונקציה המשותפת עם ולידציה כנגד SCHEDULE_INTERVALS ובדיקת "off". https://claude.ai/code/session_019psqKqV6MhkHQRkqt8Szz5
האינדקס (user_id, file_id) הוחלף מ-user_file_lookup
ל-file_bookmarks_user_file_idx, אבל הישן לא נמחק.
MongoDB לא מוחק אינדקס ישן אוטומטית כשיוצרים חדש
באותם שדות עם שם אחר — נשארים שני אינדקסים.
הוסף drop_index("user_file_lookup") ליד ה-drop הקיים.
https://claude.ai/code/session_019psqKqV6MhkHQRkqt8Szz5
_extract_schedule_key תפס ImportError והחזיר None תמיד, מה שגרם ל-scheduler לתפוס משתמשים ולאפס מיד (sentinel reset) בלי להריץ גיבוי — שקט ולנצח. הוסף fallback מקומי שבודק schedule_key, scheduleKey, schedule ישירות מ-drive_prefs כשה-import נכשל. https://claude.ai/code/session_019psqKqV6MhkHQRkqt8Szz5
_extract_schedule_key נקרא מחוץ ל-try/except בלולאת הסריקה. אם זורק exception (למשל מתוך extract_schedule_key המיובא), כל הלולאה נעצרת — ה-sentinel לא מאופס והמשתמשים הנותרים נדלגים. תיקון: - עטיפה ב-try/except סביב _extract_schedule_key בלולאת הסריקה - שינוי except ImportError ל-except Exception בפונקציה עצמה כך שגם שגיאות מתוך extract_schedule_key נתפסות https://claude.ai/code/session_019psqKqV6MhkHQRkqt8Szz5
1. backup_api.py: restore_backup_async לא בדק אם כבר רץ restore לאותו user. שתי קריאות מקבילות גורמות לכתיבות כפולות ל-DB. הוסף בדיקת status: "running" לפני יצירת job חדש (409). 2. backup_scheduler.py: _scan_drive_backups הוא while True שרץ לפני _scan_disk_backups. אחרי השבתה ארוכה, הרבה drive backups בתור גורמים ל-disk להירעב שעות. הוסף MAX_BACKUPS_PER_SCAN (10) — כל סוג מעבד עד 10 בסריקה, והנותרים ייתפסו בסריקה הבאה. https://claude.ai/code/session_019psqKqV6MhkHQRkqt8Szz5
הסרת width: 100% מכפתורי הגיבוי ומה-select בסקשנים של Drive ודיסק, והחלפת flex: 1 ב-div פשוט כך שהאלמנטים מתאימים לטקסט שבתוכם. https://claude.ai/code/session_019psqKqV6MhkHQRkqt8Szz5
🧯 Dangerous deletes guard reportPolicy: see .cursorrules — dangerous deletions are blocked unless wrapped safely. Summary:
Flagged findings (file:line:snippet): Excluded matches (by path pattern) |
⏱️ Performance report(No performance test durations collected. Mark tests with |
💡 Codex ReviewLines 322 to 326 in e70116f The async restore guard is a non-atomic check-then-insert: this CodeBot/webapp/backup_scheduler.py Lines 91 to 94 in e70116f Disk backup filenames are only second-granular ( CodeBot/webapp/drive_backup_api.py Lines 117 to 119 in e70116f This endpoint waits only 60 seconds for ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
1. P1 — restore job claim: unique partial index (status=running, per user) replaces non-atomic check-then-insert with DuplicateKeyError catch 2. P2 — disk backup filename: add microseconds (%f) to prevent overwrites 3. P2 — Drive backup-now: returns immediately with status=running, background thread updates DB on completion, UI polls backup-status https://claude.ai/code/session_019psqKqV6MhkHQRkqt8Szz5
📖 Documentation PreviewThe documentation has been built successfully!
To view locally:
|
The backend now returns immediately with status=running, so the JS must poll /api/drive/backup-status for manual_backup_status instead of treating ok=true as completion. Polls every 1s for up to 2 minutes. https://claude.ai/code/session_019psqKqV6MhkHQRkqt8Szz5
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Prevents TTL index failure from silently skipping the critical unique partial index that guards concurrent restores. https://claude.ai/code/session_019psqKqV6MhkHQRkqt8Szz5

תבנית Pull Request
✨ תיאור קצר
📦 שינויים עיקריים
פירוט נקודות (רשימת תבליטים):
🧪 בדיקות
🧪 בדיקות נדרשות ב‑PR
📝 סוג שינוי
דוגמאות Conventional Commits
✅ צ'קליסט
services/register_jobs.py(כולל Callback/Trigger להפעלה ידנית — למשלcallback_name/trigger_funcלפי המבנה) כדי שיופיעו בדשבורדdocs/environment-variables.rstוגםservices/config_inspector_service.pydocs/webapp/theming_and_css.rst+FEATURE_SUGGESTIONS/theme_matrix.md🧩 השפעות/סיכונים
🔗 קישורים
🧯 סיכון / החזרה לאחור (Rollback)