mirror of
https://github.com/GSA/notifications-admin.git
synced 2026-08-19 05:59:44 -04:00
Optimizing polling (#2946)
* Optimizing polling * Fixed formatting issue
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import json
|
||||
import os
|
||||
from functools import partial
|
||||
|
||||
@@ -67,12 +68,6 @@ def view_job(service_id, job_id):
|
||||
FEATURE_SOCKET_ENABLED=current_app.config["FEATURE_SOCKET_ENABLED"],
|
||||
job=job,
|
||||
status=request.args.get("status", ""),
|
||||
updates_url=url_for(
|
||||
".view_job_updates",
|
||||
service_id=service_id,
|
||||
job_id=job.id,
|
||||
status=request.args.get("status", ""),
|
||||
),
|
||||
partials=get_job_partials(job),
|
||||
)
|
||||
|
||||
@@ -112,11 +107,48 @@ def cancel_job(service_id, job_id):
|
||||
return redirect(url_for("main.service_dashboard", service_id=service_id))
|
||||
|
||||
|
||||
@main.route("/services/<uuid:service_id>/jobs/<uuid:job_id>/status.json")
|
||||
@user_has_permissions()
|
||||
def view_job_status_poll(service_id, job_id):
|
||||
"""
|
||||
Poll status endpoint that only queries jobs table.
|
||||
Returns minimal data needed for polling.
|
||||
"""
|
||||
import time
|
||||
|
||||
start_time = time.time()
|
||||
|
||||
job = Job.from_id(job_id, service_id=service_id)
|
||||
|
||||
processed_count = job.notifications_delivered + job.notifications_failed
|
||||
total_count = job.notification_count
|
||||
|
||||
response_data = {
|
||||
"sent_count": job.notifications_delivered,
|
||||
"failed_count": job.notifications_failed,
|
||||
"pending_count": job.notifications_sending,
|
||||
"total_count": total_count,
|
||||
"finished": job.finished_processing,
|
||||
}
|
||||
|
||||
response_time_ms = round((time.time() - start_time) * 1000, 2)
|
||||
response_json = json.dumps(response_data)
|
||||
response_size_bytes = len(response_json.encode("utf-8"))
|
||||
|
||||
current_app.logger.info(
|
||||
f"Poll status request - job_id={job_id[:8]} "
|
||||
f"response_size={response_size_bytes}b "
|
||||
f"response_time={response_time_ms}ms "
|
||||
f"progress={processed_count}/{total_count}"
|
||||
)
|
||||
|
||||
return jsonify(response_data)
|
||||
|
||||
|
||||
@main.route("/services/<uuid:service_id>/jobs/<uuid:job_id>.json")
|
||||
@user_has_permissions()
|
||||
def view_job_updates(service_id, job_id):
|
||||
job = Job.from_id(job_id, service_id=service_id)
|
||||
|
||||
return jsonify(**get_job_partials(job))
|
||||
|
||||
|
||||
|
||||
@@ -92,7 +92,11 @@ def organization_dashboard(org_id):
|
||||
def download_organization_usage_report(org_id):
|
||||
selected_year_input = request.args.get("selected_year")
|
||||
# Validate selected_year to prevent header injection
|
||||
if selected_year_input and selected_year_input.isdigit() and len(selected_year_input) == 4:
|
||||
if (
|
||||
selected_year_input
|
||||
and selected_year_input.isdigit()
|
||||
and len(selected_year_input) == 4
|
||||
):
|
||||
selected_year = selected_year_input
|
||||
else:
|
||||
selected_year = str(datetime.now().year)
|
||||
@@ -128,8 +132,9 @@ def download_organization_usage_report(org_id):
|
||||
|
||||
# Sanitize organization name for filename to prevent header injection
|
||||
import re
|
||||
safe_org_name = re.sub(r'[^\w\s-]', '', current_organization.name).strip()
|
||||
safe_org_name = re.sub(r'[-\s]+', '-', safe_org_name)
|
||||
|
||||
safe_org_name = re.sub(r"[^\w\s-]", "", current_organization.name).strip()
|
||||
safe_org_name = re.sub(r"[-\s]+", "-", safe_org_name)
|
||||
|
||||
return (
|
||||
Spreadsheet.from_rows(org_usage_data).as_csv_data,
|
||||
|
||||
@@ -198,16 +198,14 @@ def process_folder_management_form(form, current_folder_id):
|
||||
# Use request.full_path which includes query string but not host
|
||||
# This avoids host header injection while preserving all parameters
|
||||
# Hardened redirect: only allow relative URLs, and strip any backslashes
|
||||
target = request.full_path.replace('\\', '')
|
||||
target = request.full_path.replace("\\", "")
|
||||
parts = urlparse(target)
|
||||
if not parts.scheme and not parts.netloc and target.startswith('/'):
|
||||
if not parts.scheme and not parts.netloc and target.startswith("/"):
|
||||
return redirect(target)
|
||||
# Fallback to main template list for this service
|
||||
return redirect(url_for(
|
||||
'.choose_template',
|
||||
service_id=current_service.id,
|
||||
template_type='all'
|
||||
))
|
||||
return redirect(
|
||||
url_for(".choose_template", service_id=current_service.id, template_type="all")
|
||||
)
|
||||
|
||||
|
||||
def get_template_nav_label(value):
|
||||
|
||||
Reference in New Issue
Block a user