from flask import Flask, render_template, request, jsonify from flask_cors import CORS from datetime import datetime import os app = Flask(__name__) CORS(app) # In-memory store for the frontend base URL. # The Cloudflare Worker calls /api/update-urls whenever the frontend URL changes. current_urls = { 'base_url': '', 'last_updated': '', 'portals': [] } # All 10 AEGIS analysis windows + their metadata WINDOW_DEFINITIONS = [ { 'window': 2, 'name': 'Tech Analysis', 'path': '/window2', 'icon': 'cpu', 'color': 'blue', 'description': 'TEC model — technology threat scores for any year', }, { 'window': 3, 'name': 'Conductor Synthesis', 'path': '/window3', 'icon': 'git-branch', 'color': 'purple', 'description': 'Groq synthesis of tech threats into strategic insights', }, { 'window': 4, 'name': 'Economic Analysis', 'path': '/window4', 'icon': 'trending-up', 'color': 'yellow', 'description': '12 economic impact indicators from conductor results', }, { 'window': 5, 'name': 'War Analysis', 'path': '/window5', 'icon': 'shield', 'color': 'red', 'description': 'Dual AI + live conflict data — war risk predictions', }, { 'window': 6, 'name': 'Climate Analysis', 'path': '/window6', 'icon': 'cloud', 'color': 'cyan', 'description': 'World Bank CCKP — climate-conflict nexus assessment', }, { 'window': 7, 'name': 'Disease Prediction', 'path': '/window7', 'icon': 'activity', 'color': 'orange', 'description': 'AI biosecurity risk — top 3 disease outbreak predictions', }, { 'window': 8, 'name': 'Drug Discovery', 'path': '/window8', 'icon': 'zap', 'color': 'green', 'description': '4-phase pipeline — SMILES, DiffDock, Vina, manufacturing', }, { 'window': 9, 'name': 'Visual Disease', 'path': '/window9', 'icon': 'image', 'color': 'pink', 'description': 'AI-generated pathogen morphology visualizations', }, { 'window': 10, 'name': 'Visual Kinetic', 'path': '/window10', 'icon': 'eye', 'color': 'indigo', 'description': '3D PK/PD simulation with live cardiac monitoring', }, { 'window': 11, 'name': 'AR Portal', 'path': '/window11', 'icon': 'scan', 'color': 'teal', 'description': 'Augmented Reality — scan QR to view all windows remotely', }, ] def build_portals(base_url: str) -> list: """Build portal list from window definitions + a given base URL.""" return [ { **w, 'url': f"{base_url.rstrip('/')}{w['path']}", 'qr_url': f"{base_url.rstrip('/')}{w['path']}", } for w in WINDOW_DEFINITIONS ] # ── Routes ──────────────────────────────────────────────────────────────────── @app.route('/') def index(): """AR Portal Dashboard — shows QR grid for all windows.""" return render_template( 'ar_dashboard.html', base_url=current_urls.get('base_url', ''), last_updated=current_urls.get('last_updated', ''), portals=current_urls.get('portals', []), ) @app.route('/ping', methods=['GET']) def ping(): """UptimeRobot keep-alive — returns 200 instantly, no heavy work.""" return jsonify({'status': 'ok', 'message': 'AR Space is alive'}), 200 @app.route('/health') def health(): """Health check.""" return jsonify({'status': 'healthy', 'timestamp': datetime.now().isoformat()}) @app.route('/api/status') def status(): """Current AR system status — called by the worker.""" return jsonify({ 'status': 'online', 'base_url': current_urls.get('base_url', ''), 'last_updated': current_urls.get('last_updated', ''), 'portals_count': len(current_urls.get('portals', [])), 'total_windows': len(WINDOW_DEFINITIONS), }) @app.route('/api/update-urls', methods=['POST']) def update_urls(): """ Called by the Cloudflare Worker (or directly) to register the frontend base URL. Once set, /api/qr-data returns ready-to-use QR links. """ try: data = request.get_json() if not data or 'base_url' not in data: return jsonify({'error': 'base_url is required'}), 400 base_url = data['base_url'].rstrip('/') portals = build_portals(base_url) timestamp = datetime.now().isoformat() current_urls.update({ 'base_url': base_url, 'last_updated': timestamp, 'portals': portals, }) return jsonify({ 'success': True, 'message': 'URLs updated successfully', 'base_url': base_url, 'timestamp': timestamp, 'portals_count': len(portals), }) except Exception as e: return jsonify({'error': str(e)}), 500 @app.route('/api/qr-data', methods=['GET']) def qr_data(): """ Returns all window URLs ready for QR-code generation. Window 11 frontend calls this via the worker proxy to render the QR grid. Optional ?window=N to get a single window's data. """ window_param = request.args.get('window') base_url = current_urls.get('base_url', '') if not base_url: return jsonify({ 'error': 'base_url not configured yet', 'hint': 'POST {"base_url": "https://your-app.com"} to /api/update-urls', 'portals': [], }), 503 portals = current_urls.get('portals') or build_portals(base_url) if window_param: try: wnum = int(window_param) portal = next((p for p in portals if p['window'] == wnum), None) if not portal: return jsonify({'error': f'Window {wnum} not found'}), 404 return jsonify({'success': True, 'portal': portal}) except ValueError: return jsonify({'error': 'window must be an integer'}), 400 return jsonify({ 'success': True, 'base_url': base_url, 'last_updated': current_urls.get('last_updated', ''), 'portals': portals, 'total': len(portals), }) @app.route('/api/windows', methods=['GET']) def windows(): """Static list of all window definitions (no base_url needed).""" return jsonify({ 'success': True, 'windows': WINDOW_DEFINITIONS, 'total': len(WINDOW_DEFINITIONS), }) if __name__ == '__main__': port = int(os.environ.get('PORT', 7860)) app.run(host='0.0.0.0', port=port, debug=False)