Compare commits
2 Commits
64f686e275
...
702de68098
| Author | SHA1 | Date | |
|---|---|---|---|
| 702de68098 | |||
| 56f904bde3 |
@ -17,4 +17,4 @@ ENV APP_PORT=8080
|
||||
EXPOSE 8080
|
||||
|
||||
# Use the application factory from backend.app
|
||||
CMD ["gunicorn", "-b", "0.0.0.0:8080", "backend.app:create_app()"]
|
||||
CMD ["gunicorn", "-b", "0.0.0.0:8080", "--timeout", "120", "backend.app:create_app()"]
|
||||
|
||||
@ -31,6 +31,7 @@ def cloud_connect_accounts():
|
||||
)
|
||||
|
||||
customers = Customer.query.filter_by(active=True).order_by(Customer.name.asc()).all()
|
||||
customer_rows = [{"id": c.id, "name": c.name} for c in customers]
|
||||
jobs = Job.query.filter_by(archived=False).order_by(Job.job_name.asc()).all()
|
||||
|
||||
# Attach derived fields for the template
|
||||
@ -45,7 +46,7 @@ def cloud_connect_accounts():
|
||||
"main/cloud_connect_accounts.html",
|
||||
unmatched=unmatched,
|
||||
matched=matched,
|
||||
customers=customers,
|
||||
customers=customer_rows,
|
||||
jobs=jobs,
|
||||
)
|
||||
|
||||
|
||||
@ -8,118 +8,57 @@ from datetime import datetime
|
||||
@roles_required("admin")
|
||||
def settings_jobs_delete_all():
|
||||
try:
|
||||
jobs = Job.query.all()
|
||||
job_count = db.session.execute(text("SELECT COUNT(*) FROM jobs")).scalar() or 0
|
||||
|
||||
if not jobs:
|
||||
if not job_count:
|
||||
flash("No jobs to delete.", "info")
|
||||
return redirect(url_for("main.settings", section="general"))
|
||||
return redirect(url_for("main.settings"))
|
||||
|
||||
|
||||
|
||||
# Collect run ids for FK cleanup in auxiliary tables that may not have ON DELETE CASCADE
|
||||
run_ids = []
|
||||
mail_message_ids = []
|
||||
|
||||
for job in jobs:
|
||||
for run in job.runs:
|
||||
if run.id is not None:
|
||||
run_ids.append(run.id)
|
||||
if run.mail_message_id:
|
||||
mail_message_ids.append(run.mail_message_id)
|
||||
|
||||
# Return related mails back to inbox and unlink from job
|
||||
if mail_message_ids:
|
||||
msgs = MailMessage.query.filter(MailMessage.id.in_(mail_message_ids)).all()
|
||||
for msg in msgs:
|
||||
if hasattr(msg, "location"):
|
||||
msg.location = "inbox"
|
||||
msg.job_id = None
|
||||
|
||||
def _safe_execute(stmt, params):
|
||||
def _try(stmt):
|
||||
"""Best-effort: skip if table/column doesn't exist in this schema version."""
|
||||
try:
|
||||
db.session.execute(stmt, params)
|
||||
except Exception as cleanup_exc:
|
||||
# Best-effort cleanup for differing DB schemas
|
||||
print(f"[settings-jobs] Cleanup skipped: {cleanup_exc}")
|
||||
db.session.execute(text(stmt))
|
||||
except Exception as exc:
|
||||
print(f"[settings-jobs] Skipped: {exc}")
|
||||
|
||||
# All deletions via direct SQL — no ORM loading into Python memory.
|
||||
# Order: unlink → FK-dependent tables → job_runs → jobs.
|
||||
|
||||
# Ensure run_object_links doesn't block job_runs deletion (older schemas may miss ON DELETE CASCADE)
|
||||
if run_ids:
|
||||
db.session.execute(
|
||||
text("DELETE FROM run_object_links WHERE run_id IN :run_ids").bindparams(
|
||||
bindparam("run_ids", expanding=True)
|
||||
),
|
||||
{"run_ids": run_ids},
|
||||
)
|
||||
# 1. Unlink staging accounts that have a nullable FK to jobs.
|
||||
_try("UPDATE cove_accounts SET job_id = NULL WHERE job_id IS NOT NULL")
|
||||
_try("UPDATE cloud_connect_accounts SET job_id = NULL WHERE job_id IS NOT NULL")
|
||||
|
||||
# 2. Return mail messages linked to jobs back to the inbox.
|
||||
db.session.execute(text(
|
||||
"UPDATE mail_messages SET job_id = NULL, location = 'inbox' WHERE job_id IS NOT NULL"
|
||||
))
|
||||
|
||||
# 3. Delete tables that FK → job_runs.
|
||||
_try("DELETE FROM remark_job_runs")
|
||||
_try("DELETE FROM ticket_job_runs")
|
||||
_try("DELETE FROM run_object_links")
|
||||
|
||||
# Ensure job_object_links doesn't block jobs deletion (older schemas may miss ON DELETE CASCADE)
|
||||
job_ids = [j.id for j in jobs]
|
||||
if job_ids:
|
||||
db.session.execute(
|
||||
text("DELETE FROM job_object_links WHERE job_id IN :job_ids").bindparams(
|
||||
bindparam("job_ids", expanding=True)
|
||||
),
|
||||
{"job_ids": job_ids},
|
||||
)
|
||||
# 4. Delete tables that FK → jobs.
|
||||
_try("DELETE FROM job_object_links")
|
||||
_try("DELETE FROM ticket_scopes WHERE job_id IS NOT NULL")
|
||||
_try("DELETE FROM remark_scopes WHERE job_id IS NOT NULL OR job_run_id IS NOT NULL")
|
||||
_try("DELETE FROM overrides WHERE job_id IS NOT NULL")
|
||||
|
||||
# Clean up auxiliary FK tables that may reference job_runs/jobs without ON DELETE CASCADE (older schemas)
|
||||
if run_ids:
|
||||
_safe_execute(
|
||||
text("DELETE FROM remark_job_runs WHERE job_run_id IN :run_ids").bindparams(
|
||||
bindparam("run_ids", expanding=True)
|
||||
),
|
||||
{"run_ids": run_ids},
|
||||
)
|
||||
_safe_execute(
|
||||
text("DELETE FROM ticket_job_runs WHERE job_run_id IN :run_ids").bindparams(
|
||||
bindparam("run_ids", expanding=True)
|
||||
),
|
||||
{"run_ids": run_ids},
|
||||
)
|
||||
# Some schemas use remark_scopes for per-run remarks
|
||||
_safe_execute(
|
||||
text("DELETE FROM remark_scopes WHERE job_run_id IN :run_ids").bindparams(
|
||||
bindparam("run_ids", expanding=True)
|
||||
),
|
||||
{"run_ids": run_ids},
|
||||
)
|
||||
|
||||
if job_ids:
|
||||
# ticket_scopes.job_id is a FK without ON DELETE CASCADE in some schemas
|
||||
_safe_execute(
|
||||
text("DELETE FROM ticket_scopes WHERE job_id IN :job_ids").bindparams(
|
||||
bindparam("job_ids", expanding=True)
|
||||
),
|
||||
{"job_ids": job_ids},
|
||||
)
|
||||
|
||||
# Some schemas use remark_scopes for per-job remarks
|
||||
_safe_execute(
|
||||
text("DELETE FROM remark_scopes WHERE job_id IN :job_ids").bindparams(
|
||||
bindparam("job_ids", expanding=True)
|
||||
),
|
||||
{"job_ids": job_ids},
|
||||
)
|
||||
# Overrides may reference jobs directly
|
||||
_safe_execute(
|
||||
text("DELETE FROM overrides WHERE job_id IN :job_ids").bindparams(
|
||||
bindparam("job_ids", expanding=True)
|
||||
),
|
||||
{"job_ids": job_ids},
|
||||
)
|
||||
|
||||
# Delete all jobs (runs/objects are cascaded via ORM relationships)
|
||||
for job in jobs:
|
||||
db.session.delete(job)
|
||||
# 5. Delete runs, then jobs.
|
||||
db.session.execute(text("DELETE FROM job_runs"))
|
||||
db.session.execute(text("DELETE FROM jobs"))
|
||||
|
||||
db.session.commit()
|
||||
flash("All jobs deleted. Related mails are returned to the inbox.", "success")
|
||||
|
||||
_log_admin_event(
|
||||
event_type="jobs_delete_all",
|
||||
message=f"Deleted all {job_count} job(s) and all related data.",
|
||||
)
|
||||
flash(f"All {job_count} jobs deleted. Related mails are returned to the inbox.", "success")
|
||||
except Exception as exc:
|
||||
db.session.rollback()
|
||||
print(f"[settings-jobs] Failed to delete all jobs: {exc}")
|
||||
flash("Failed to delete all jobs.", "danger")
|
||||
flash(f"Failed to delete all jobs: {exc}", "danger")
|
||||
|
||||
return redirect(url_for("main.settings"))
|
||||
|
||||
|
||||
@ -163,7 +163,7 @@
|
||||
autocomplete="off" />
|
||||
<datalist id="ccCustomerList">
|
||||
{% for c in customers %}
|
||||
<option value="{{ c.name }}"></option>
|
||||
<option value="{{ c.name | e }}"></option>
|
||||
{% endfor %}
|
||||
</datalist>
|
||||
</div>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user