import os import sys from flask import Flask, render_template_string, jsonify, request import markdown import time import gzip import io app = Flask(__name__) # Cache storage: filepath -> (mtime, html_content) _content_cache = {} # Constants for paths to avoid re-calculating on every request BASE_DIR = os.path.dirname(os.path.abspath(__file__)) README_PATH = os.path.join(BASE_DIR, '..', 'README.md') VERIFICATION_PATH = os.path.join(BASE_DIR, '..', 'VERIFICATION.md') # HTML Template DASHBOARD_HTML = """ MQL5 Trading Automation Dashboard

System Status ONLINE

MQL5 Trading Automation is running.

{{ html_verification|safe }}

Project Documentation

{{ html_readme|safe }}
""" # Global to store compiled template DASHBOARD_TEMPLATE = None def get_cached_markdown(filepath): """ Returns the markdown content of a file converted to HTML, using a cache that invalidates based on file modification time. Optimization: Uses os.stat() to get mtime and check existence in one syscall. """ try: # Optimization: os.stat gets existence and mtime in one call # removing the need for separate os.path.exists() check stat_result = os.stat(filepath) except OSError: return None try: mtime = stat_result.st_mtime if filepath in _content_cache: cached_mtime, cached_html = _content_cache[filepath] if cached_mtime == mtime: return cached_html # Cache miss or file changed with open(filepath, 'r', encoding='utf-8') as f: content = f.read() html_content = markdown.markdown(content) _content_cache[filepath] = (mtime, html_content) return html_content except Exception as e: print(f"Error reading/converting {filepath}: {e}") return None @app.route('/health') def health_check(): """Lightweight health check for load balancers.""" return jsonify({ "status": "healthy", "timestamp": time.time() }) @app.after_request def add_security_headers(response): """ Add security headers to every response to protect against XSS, Clickjacking, and other web vulnerabilities. """ # Content-Security-Policy: restrict sources of content # default-src 'self': only allow content from own origin # style-src 'self' 'unsafe-inline': allow inline styles (needed for template) # script-src 'self': only allow scripts from own origin (blocks inline scripts in markdown) csp = "default-src 'self'; style-src 'self' 'unsafe-inline'; script-src 'self'" response.headers['Content-Security-Policy'] = csp # X-Content-Type-Options: prevent MIME-sniffing response.headers['X-Content-Type-Options'] = 'nosniff' # X-Frame-Options: prevent clickjacking response.headers['X-Frame-Options'] = 'SAMEORIGIN' # Referrer-Policy: control referrer information response.headers['Referrer-Policy'] = 'strict-origin-when-cross-origin' return response @app.after_request def compress_response(response): """ Compress response data with gzip if the client accepts it and the content is large enough. ⚡ Performance Optimization: Reduces payload size by ~70% for text content. """ try: # Check if client accepts gzip accept_encoding = request.headers.get('Accept-Encoding', '') if 'gzip' not in accept_encoding.lower(): return response # Check content type compatibility content_type = response.headers.get('Content-Type', '') compressible_types = ['text/html', 'text/css', 'text/plain', 'application/json', 'application/javascript'] if not any(ctype in content_type for ctype in compressible_types): return response # Check status code (only compress success) if response.status_code < 200 or response.status_code >= 300: return response # Check content length (avoid overhead for tiny responses) # Note: response.data is bytes content_length = len(response.data) if content_length < 500: return response # Perform compression gzip_buffer = io.BytesIO() with gzip.GzipFile(mode='wb', compresslevel=5, fileobj=gzip_buffer) as gzip_file: gzip_file.write(response.data) compressed_data = gzip_buffer.getvalue() # Update response response.set_data(compressed_data) response.headers['Content-Encoding'] = 'gzip' response.headers['Content-Length'] = len(compressed_data) # Add Vary header to ensure proxies handle compression correctly response.headers['Vary'] = 'Accept-Encoding' return response except Exception as e: # Fallback to uncompressed on error print(f"Compression failed: {e}") return response @app.route('/') def dashboard(): global DASHBOARD_TEMPLATE try: # Use pre-calculated paths html_readme = get_cached_markdown(README_PATH) or "

README.md not found.

" html_verification = get_cached_markdown(VERIFICATION_PATH) or "

VERIFICATION.md not found.

" # ⚡ Performance Optimization: Compile template once instead of every request if DASHBOARD_TEMPLATE is None: DASHBOARD_TEMPLATE = app.jinja_env.from_string(DASHBOARD_HTML) return DASHBOARD_TEMPLATE.render(html_readme=html_readme, html_verification=html_verification, year=2026) except Exception as e: return f"Error: {str(e)}", 500 if __name__ == '__main__': port = int(os.environ.get('PORT', 8080)) print(f"Starting web dashboard on port {port}...") app.run(host='0.0.0.0', port=port)