""" MkDocs Hook for Environment Variable Injection Injects environment variables into the site as JavaScript configuration. This allows client-side code to access server-side config values. """ import os import logging from typing import Dict, Any # Configure logging logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) def on_config(config: Dict[str, Any]) -> Dict[str, Any]: """ Hook that runs when MkDocs loads the configuration. Injects environment variables as extra JavaScript. """ import re # Read environment variables (with fallbacks) media_api_url = os.environ.get('MEDIA_API_PUBLIC_URL', 'http://localhost:4100') media_api_port = os.environ.get('MEDIA_API_PORT', '4100') admin_port = os.environ.get('ADMIN_PORT', '3000') admin_url = os.environ.get('ADMIN_URL', '') base_domain = os.environ.get('BASE_DOMAIN', '') if base_domain and not base_domain.startswith('http'): base_domain = f'https://{base_domain}' # Helper: detect Docker container hostnames (not browser-accessible) def is_docker_hostname(url: str) -> bool: """Check if URL uses a Docker container hostname instead of localhost/domain.""" host = re.sub(r'^https?://', '', url).split(':')[0].split('/')[0] # Docker hostnames typically contain hyphens and no dots (not localhost, not a domain) return host != 'localhost' and '.' not in host # Resolve media API URL — must be browser-accessible (not Docker hostname) if is_docker_hostname(media_api_url): media_api_url = f'http://localhost:{media_api_port}' # Resolve public URL (admin app) if admin_url and not is_docker_hostname(admin_url) and 'localhost' not in admin_url: # Production domain — use as-is public_url = admin_url else: # Dev: use ADMIN_PORT (most reliable source of truth) public_url = f'http://localhost:{admin_port}' # Create inline JavaScript with config config_script = f""" // Auto-generated video player configuration from environment variables // Generated by mkdocs/docs/hooks/env_config_hook.py (function() {{ window.MEDIA_API_URL = '{media_api_url}'; window.PUBLIC_URL = '{public_url}'; window.VIDEO_PLAYER_DEBUG = {str(os.environ.get('VIDEO_PLAYER_DEBUG', 'false')).lower()}; console.log('[Video Config] Loaded from environment:', {{ mediaApiUrl: window.MEDIA_API_URL, publicUrl: window.PUBLIC_URL, debug: window.VIDEO_PLAYER_DEBUG }}); }})(); """.strip() # Inject as extra_javascript (inline script) # MkDocs will include this before other scripts if 'extra_javascript' not in config: config['extra_javascript'] = [] # Prepend inline config script (load before video-player.js) # Note: We'll need to create a file for this env_config_path = 'assets/js/env-config.js' # Write the generated config to a file (only if content changed to avoid # triggering MkDocs file watcher rebuild loop in serve mode) import pathlib docs_dir = pathlib.Path(config['docs_dir']) env_config_file = docs_dir / env_config_path env_config_file.parent.mkdir(parents=True, exist_ok=True) existing_content = '' if env_config_file.exists(): existing_content = env_config_file.read_text() if existing_content != config_script: with open(env_config_file, 'w') as f: f.write(config_script) logger.info(f"✓ Generated video config: {env_config_file}") logger.info(f" MEDIA_API_URL: {media_api_url}") logger.info(f" PUBLIC_URL: {public_url}") else: logger.info(f"✓ Video config unchanged, skipping write") # Insert at the beginning of extra_javascript list if env_config_path not in config['extra_javascript']: config['extra_javascript'].insert(0, env_config_path) return config def on_pre_build(config: Dict[str, Any]) -> None: """ Hook that runs before build starts. Ensures config is regenerated on each build. """ logger.info("Video player configuration will be generated from environment variables")