diff --git a/containers/backupchecks/src/backend/app/main/routes_settings.py b/containers/backupchecks/src/backend/app/main/routes_settings.py index bb19ea7..e2a4038 100644 --- a/containers/backupchecks/src/backend/app/main/routes_settings.py +++ b/containers/backupchecks/src/backend/app/main/routes_settings.py @@ -124,6 +124,41 @@ def settings_jobs_delete_all(): return redirect(url_for("main.settings")) +@main_bp.route("/settings/jobs/orphaned", methods=["GET"]) +@login_required +@roles_required("admin") +def settings_jobs_orphaned(): + """Show list of orphaned jobs for verification before deletion.""" + # Find jobs without valid customer + orphaned_jobs = Job.query.outerjoin(Customer, Job.customer_id == Customer.id).filter( + db.or_( + Job.customer_id.is_(None), + Customer.id.is_(None) + ) + ).order_by(Job.job_name.asc()).all() + + # Build list with details + jobs_list = [] + for job in orphaned_jobs: + run_count = JobRun.query.filter_by(job_id=job.id).count() + mail_count = JobRun.query.filter_by(job_id=job.id).filter(JobRun.mail_message_id.isnot(None)).count() + + jobs_list.append({ + "id": job.id, + "job_name": job.job_name or "Unnamed", + "backup_software": job.backup_software or "-", + "backup_type": job.backup_type or "-", + "customer_id": job.customer_id, + "run_count": run_count, + "mail_count": mail_count, + }) + + return render_template( + "main/settings_orphaned_jobs.html", + orphaned_jobs=jobs_list, + ) + + @main_bp.route("/settings/jobs/delete-orphaned", methods=["POST"]) @login_required @roles_required("admin") diff --git a/containers/backupchecks/src/templates/main/settings.html b/containers/backupchecks/src/templates/main/settings.html index d6bc75a..ab581fa 100644 --- a/containers/backupchecks/src/templates/main/settings.html +++ b/containers/backupchecks/src/templates/main/settings.html @@ -554,9 +554,12 @@
Cleanup orphaned jobs

Delete jobs that are no longer linked to an existing customer. Related emails and runs will be permanently deleted from the database.

-
- -
+
+ Preview orphaned jobs +
+ +
+
diff --git a/containers/backupchecks/src/templates/main/settings_orphaned_jobs.html b/containers/backupchecks/src/templates/main/settings_orphaned_jobs.html new file mode 100644 index 0000000..1933db5 --- /dev/null +++ b/containers/backupchecks/src/templates/main/settings_orphaned_jobs.html @@ -0,0 +1,87 @@ +{% extends "layout/base.html" %} + +{% block title %}Orphaned Jobs Preview{% endblock %} + +{% block content %} +
+
+
+

Orphaned Jobs Preview

+

Jobs without a valid customer link

+
+ Back to Settings +
+ + {% if orphaned_jobs %} +
+ ⚠️ Warning: Found {{ orphaned_jobs|length }} orphaned job(s). Review the list below before deleting. +
+ +
+
+ Orphaned Jobs List +
+ +
+
+
+
+ + + + + + + + + + + + + {% for job in orphaned_jobs %} + + + + + + + + + {% endfor %} + + + + + + + + +
Job NameBackup SoftwareBackup TypeCustomer IDRunsEmails
{{ job.job_name }}{{ job.backup_software }}{{ job.backup_type }} + {% if job.customer_id %} + {{ job.customer_id }} (deleted) + {% else %} + NULL + {% endif %} + {{ job.run_count }}{{ job.mail_count }}
Total{{ orphaned_jobs|sum(attribute='run_count') }}{{ orphaned_jobs|sum(attribute='mail_count') }}
+
+
+
+ +
+ ℹ️ What will be deleted: + +
+ + {% else %} +
+ ✅ No orphaned jobs found. +

All jobs are properly linked to existing customers.

+
+ {% endif %} +
+{% endblock %} diff --git a/docs/changelog-claude.md b/docs/changelog-claude.md index 0798f7c..f46ffe1 100644 --- a/docs/changelog-claude.md +++ b/docs/changelog-claude.md @@ -6,6 +6,7 @@ This file documents all changes made to this project via Claude Code. ### Added - Added "Cleanup orphaned jobs" maintenance option in Settings → Maintenance to delete jobs without valid customer links and their associated emails/runs permanently from database (useful when customers are removed) +- Added "Preview orphaned jobs" button to show detailed list of jobs to be deleted with run/email counts before confirming deletion (verification step for safety) ### Changed - Removed customer name from Autotask ticket title to keep titles concise (format changed from "[Backupchecks] Customer - Job Name - Status" to "[Backupchecks] Job Name - Status")