From 03c9ca088cb2b59ed7cc7507764e9cacf7b9e197 Mon Sep 17 00:00:00 2001 From: NguyenND Date: Thu, 16 Apr 2026 13:11:28 -0400 Subject: [PATCH] 04/16 Updated with domain settings in .env file --- app.py | 27 +++++++++++++++++---------- config.py | 5 +++++ routes/qr_codes.py | 28 +++++++++++++++++++++++----- 3 files changed, 45 insertions(+), 15 deletions(-) diff --git a/app.py b/app.py index f2bd3e5..e643b2e 100644 --- a/app.py +++ b/app.py @@ -294,8 +294,10 @@ def update_existing_qr_codes(): """Update existing QR codes with missing URLs or images at startup. Regenerates qr_url slugs without needing a request context. - For qr_code_image, constructs the base URL from FLASK_HOST/FLASK_PORT - config so this can run safely outside any HTTP request. + For qr_code_image, uses QR_BASE_URL (from .env) as the authoritative base + URL so that generated links always match the public-facing domain. + Falls back to FLASK_HOST/FLASK_PORT construction only when QR_BASE_URL is + not configured (development environments without a reverse proxy). """ from extensions import db as _db, logger_handler as lh from utils.helpers import generate_qr_code, get_qr_styling, generate_qr_url @@ -306,14 +308,19 @@ def update_existing_qr_codes(): if not qr_codes: return - # Build a base URL that does not require an active request context. - host = os.environ.get('FLASK_HOST', '0.0.0.0') - # 0.0.0.0 is a bind address, not a reachable hostname — default to localhost - if host in ('0.0.0.0', ''): - host = 'localhost' - port = os.environ.get('FLASK_PORT', '5000') - scheme = 'https' if _Cfg.SESSION_COOKIE_SECURE else 'http' - base_url = f"{scheme}://{host}:{port}/" + # Prefer the explicit QR_BASE_URL env var (required behind a reverse proxy). + # Fall back to FLASK_HOST/PORT for local/dev environments. + qr_base_url = os.environ.get('QR_BASE_URL', '').rstrip('/') + if qr_base_url: + base_url = qr_base_url + '/' + else: + host = os.environ.get('FLASK_HOST', '0.0.0.0') + # 0.0.0.0 is a bind address, not a reachable hostname — default to localhost + if host in ('0.0.0.0', ''): + host = 'localhost' + port = os.environ.get('FLASK_PORT', '5000') + scheme = 'https' if _Cfg.SESSION_COOKIE_SECURE else 'http' + base_url = f"{scheme}://{host}:{port}/" updated_count = 0 for qr_code in qr_codes: diff --git a/config.py b/config.py index 5b77e79..6e3c23c 100644 --- a/config.py +++ b/config.py @@ -60,6 +60,11 @@ class Config: # ------------------------------------------------------------------ # COMPANY_NAME = os.environ.get('COMPANY_NAME', 'QR Code Management System') CONTRACT_NAME = os.environ.get('CONTRACT_NAME', 'Default Contract') + # Public-facing base URL used when constructing QR code destination links. + # Set this to your domain with no trailing slash, e.g.: + # QR_BASE_URL=https://qr.govservicesinc.com + # This prevents doubled-hostname issues when running behind a reverse proxy. + QR_BASE_URL = os.environ.get('QR_BASE_URL', '').rstrip('/') # ------------------------------------------------------------------ # # File uploads diff --git a/routes/qr_codes.py b/routes/qr_codes.py index f31f30f..eb6dcb8 100644 --- a/routes/qr_codes.py +++ b/routes/qr_codes.py @@ -42,6 +42,24 @@ import openpyxl bp = Blueprint('qr_codes', __name__) +def _get_qr_base_url(): + """ + Return the authoritative base URL for QR code destination links. + Prefers the explicit QR_BASE_URL config value (set via .env / config.py). + Falls back gracefully to request.url_root if not configured. + + Using an explicit QR_BASE_URL is required when the app runs behind a + reverse proxy (e.g. nginx) that can cause request.url_root to produce + a doubled hostname such as: + https://qr.govservicesinc.com,qr.govservicesinc.com/ + """ + configured = current_app.config.get('QR_BASE_URL', '').rstrip('/') + if configured: + return configured + '/' + # Fallback: normalise request.url_root to avoid any trailing-slash issues + return request.url_root.rstrip('/') + '/' + + # --- ADDED: helper — returns distinct (location, location_address) pairs from # all standard QR codes, used to auto-populate the dynamic QR location list --- def get_unique_qr_locations(): @@ -193,7 +211,7 @@ def create_qr_code(): qr_url = generate_qr_url(name, new_qr_code.id) # Generate QR code data with the destination URL and custom styling - qr_data = f"{request.url_root}qr/{qr_url}" + qr_data = f"{_get_qr_base_url()}qr/{qr_url}" qr_image = generate_qr_code( data=qr_data, fill_color=fill_color, @@ -321,7 +339,7 @@ def import_bulk_qr_codes(): created_by=session['user_id'], generate_qr_code_func=generate_qr_code, generate_qr_url_func=generate_qr_url, - request_url_root=request.url_root, + request_url_root=_get_qr_base_url(), project_lookup=project_lookup, QRCode=QRCode, Project=Project, @@ -543,7 +561,7 @@ def edit_qr_code(qr_id): # Regenerate QR code if name or styling changed if name_changed or styling_changed: - qr_data = f"{request.url_root}qr/{qr_code.qr_url}" + qr_data = f"{_get_qr_base_url()}qr/{qr_code.qr_url}" # Use new styling if available, otherwise use defaults styling = get_qr_styling(qr_code) @@ -1117,7 +1135,7 @@ def copy_qr_url(qr_id): return jsonify({ 'success': True, 'message': f'QR code URL copied to clipboard!', - 'url': f"{request.url_root}qr/{qr_code.qr_url}" + 'url': f"{_get_qr_base_url()}qr/{qr_code.qr_url}" }) except Exception as e: @@ -1140,7 +1158,7 @@ def open_qr_link(qr_id): return jsonify({ 'success': True, 'message': f'Opening QR code link...', - 'url': f"{request.url_root}qr/{qr_code.qr_url}" + 'url': f"{_get_qr_base_url()}qr/{qr_code.qr_url}" }) except Exception as e: