Spaces:
Running
Running
| #!/usr/bin/env python3 | |
| """ | |
| Frontend Configuration Builder | |
| Generates frontend/config.js from backend .env files to maintain single source of truth. | |
| This eliminates duplication and ensures frontend/backend configuration consistency. | |
| Usage: | |
| python scripts/build-frontend-config.py | |
| python scripts/build-frontend-config.py --env production | |
| python scripts/build-frontend-config.py --verbose | |
| """ | |
| import os | |
| import sys | |
| import json | |
| import argparse | |
| from pathlib import Path | |
| from datetime import datetime | |
| from dotenv import load_dotenv | |
| class FrontendConfigBuilder: | |
| def __init__(self, verbose=False): | |
| self.verbose = verbose | |
| self.project_root = Path(__file__).parent.parent | |
| self.environments = ["development", "production", "staging"] | |
| def log(self, message): | |
| """Print message if verbose mode is enabled""" | |
| if self.verbose: | |
| print(f"[{datetime.now().strftime('%H:%M:%S')}] {message}") | |
| def load_env_config(self, environment): | |
| """Load configuration from environment file""" | |
| self.log(f"Loading configuration for {environment} environment...") | |
| # Determine which .env file to use | |
| if environment == "development": | |
| env_file = self.project_root / ".env" | |
| else: | |
| env_file = self.project_root / f".env.{environment}" | |
| if not env_file.exists(): | |
| self.log(f"Warning: {env_file} not found, skipping {environment}") | |
| return None | |
| # Load environment variables | |
| load_dotenv(env_file, override=True) | |
| # Extract frontend-relevant configuration | |
| config = { | |
| "API_URL": os.getenv("BACKEND_API_URL", "http://127.0.0.1:8000"), | |
| "DEBUG": os.getenv("API_DEBUG", "false").lower() == "true", | |
| "SHOW_DEBUG_INFO": os.getenv("ENABLE_DEBUG_INFO", "false").lower() | |
| == "true", | |
| "ENVIRONMENT": os.getenv("ENVIRONMENT", environment), | |
| "CONFIDENCE_THRESHOLD": float(os.getenv("CONFIDENCE_THRESHOLD", "0.5")), | |
| "DEFAULT_LANGUAGE": os.getenv("DEFAULT_LANGUAGE", "en"), | |
| "REQUEST_TIMEOUT_MS": int(os.getenv("REQUEST_TIMEOUT_MS") or 30000), | |
| "API_KEY": os.getenv("DEMO_API_KEY", ""), | |
| } | |
| self.log(f"Loaded config for {environment}: API_URL={config['API_URL']}") | |
| return config | |
| def generate_config_file(self, configs, target_env=None): | |
| """Generate JavaScript configuration file""" | |
| self.log("Generating frontend configuration file...") | |
| # Filter configs if specific environment requested | |
| if target_env: | |
| if target_env in configs: | |
| configs = {target_env: configs[target_env]} | |
| else: | |
| raise ValueError( | |
| f"Environment '{target_env}' not found in loaded configurations" | |
| ) | |
| # Generate JavaScript content | |
| js_content = self._generate_js_content(configs) | |
| # Write to frontend directory | |
| output_file = self.project_root / "frontend" / "config.js" | |
| output_file.parent.mkdir(exist_ok=True) | |
| with open(output_file, "w", encoding="utf-8") as f: | |
| f.write(js_content) | |
| self.log(f"Configuration written to {output_file}") | |
| return output_file | |
| def _generate_js_content(self, configs): | |
| """Generate the JavaScript configuration content""" | |
| timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S") | |
| js_content = f"""/** | |
| * Frontend Configuration | |
| * | |
| * Auto-generated from backend .env files on {timestamp} | |
| * DO NOT EDIT THIS FILE MANUALLY - your changes will be overwritten! | |
| * | |
| * To update configuration: | |
| * 1. Edit the appropriate .env file (.env for dev, .env.production, .env.staging) | |
| * 2. Run: python scripts/build-frontend-config.py | |
| * 3. Redeploy your frontend | |
| */ | |
| // Configuration for different environments | |
| const config = {json.dumps(configs, indent=2)}; | |
| /** | |
| * Environment Detection | |
| * Automatically detects current environment based on hostname | |
| */ | |
| function detectEnvironment() {{ | |
| const hostname = window.location.hostname; | |
| // Local development | |
| if (hostname === 'localhost' || hostname === '127.0.0.1' || hostname.endsWith('.local')) {{ | |
| return 'development'; | |
| }} | |
| // Staging (if there are staging URLs) | |
| if (hostname.includes('staging')) {{ | |
| return 'staging'; | |
| }} | |
| // Default to production for live domains | |
| return 'production'; | |
| }} | |
| // Get current environment configuration | |
| const currentEnvironment = detectEnvironment(); | |
| const currentConfig = config[currentEnvironment]; | |
| if (!currentConfig) {{ | |
| console.error(`No configuration found for environment: ${{currentEnvironment}}`); | |
| console.error('Available environments:', Object.keys(config)); | |
| // Fallback to development config | |
| window.ChatbotConfig = config.development || {{}}; | |
| }} else {{ | |
| window.ChatbotConfig = currentConfig; | |
| }} | |
| // Add environment info for debugging | |
| window.ChatbotConfig.DETECTED_ENVIRONMENT = currentEnvironment; | |
| // Debug logging (only in development) | |
| if (currentConfig && currentConfig.DEBUG) {{ | |
| console.log('π€ Chatbot Configuration Loaded'); | |
| console.log('Environment:', currentEnvironment); | |
| console.log('API URL:', currentConfig.API_URL); | |
| console.log('Debug Mode:', currentConfig.DEBUG); | |
| console.log('Full Config:', currentConfig); | |
| }} | |
| // Export for module systems (if needed) | |
| if (typeof module !== 'undefined' && module.exports) {{ | |
| module.exports = {{ config, currentConfig: window.ChatbotConfig }}; | |
| }} | |
| """ | |
| return js_content | |
| def validate_configuration(self, configs): | |
| """Validate generated configuration for common issues""" | |
| self.log("Validating configuration...") | |
| issues = [] | |
| for env, config in configs.items(): | |
| # Check for missing API URLs (empty string is valid = same-origin) | |
| if config.get("API_URL") == "http://127.0.0.1:8000": | |
| if env == "production": | |
| issues.append("Production API_URL still points to localhost!") | |
| # Check for development settings in production | |
| if env == "production": | |
| if config.get("DEBUG", False): | |
| issues.append("Production has DEBUG=true (should be false)") | |
| if config.get("SHOW_DEBUG_INFO", False): | |
| issues.append( | |
| "Production has SHOW_DEBUG_INFO=true (should be false)" | |
| ) | |
| if issues: | |
| print("β οΈ Configuration Issues Found:") | |
| for issue in issues: | |
| print(f" - {issue}") | |
| print() | |
| else: | |
| self.log("β Configuration validation passed") | |
| return len(issues) == 0 | |
| def build(self, target_env=None): | |
| """Main build process""" | |
| print("π§ Building Frontend Configuration...") | |
| print(f"Project root: {self.project_root}") | |
| print() | |
| # Load configurations from all environment files | |
| configs = {} | |
| for env in self.environments: | |
| config = self.load_env_config(env) | |
| if config: | |
| configs[env] = config | |
| if not configs: | |
| print("β No valid environment configurations found!") | |
| print("Make sure you have .env files in your project root.") | |
| return False | |
| # Validate configuration | |
| self.validate_configuration(configs) | |
| # Generate configuration file | |
| output_file = self.generate_config_file(configs, target_env) | |
| print("β Frontend configuration built successfully!") | |
| print(f"π Generated: {output_file}") | |
| print(f"π Environments: {', '.join(configs.keys())}") | |
| if target_env: | |
| print(f"π― Target environment: {target_env}") | |
| print() | |
| print("Next steps:") | |
| print("1. Review the generated frontend/config.js file") | |
| print("2. Test frontend locally") | |
| print("3. Deploy frontend to Netlify or any other used frontend tools") | |
| print("4. The frontend will automatically use the correct environment!") | |
| return True | |
| def main(): | |
| parser = argparse.ArgumentParser( | |
| description="Generate frontend configuration from backend .env files", | |
| formatter_class=argparse.RawDescriptionHelpFormatter, | |
| epilog=""" | |
| Examples: | |
| python scripts/build-frontend-config.py # Build all environments | |
| python scripts/build-frontend-config.py --env production # Build only production | |
| python scripts/build-frontend-config.py --verbose # Verbose output | |
| """, | |
| ) | |
| parser.add_argument( | |
| "--env", | |
| choices=["development", "production", "staging"], | |
| help="Build configuration for specific environment only", | |
| ) | |
| parser.add_argument( | |
| "--verbose", "-v", action="store_true", help="Enable verbose output" | |
| ) | |
| args = parser.parse_args() | |
| # Create builder and run | |
| builder = FrontendConfigBuilder(verbose=args.verbose) | |
| success = builder.build(target_env=args.env) | |
| sys.exit(0 if success else 1) | |
| if __name__ == "__main__": | |
| main() | |