"""Caddy configuration generator and manager.""" import os from typing import Optional from app.models.https_config import HTTPSConfig class CaddyConfigGenerator: """Generate Caddyfile configuration based on HTTPSConfig.""" @staticmethod def generate_caddyfile(config: Optional[HTTPSConfig] = None) -> str: """Generate complete Caddyfile content. Args: config: HTTPSConfig instance or None Returns: Complete Caddyfile content as string """ # Get config from database if not provided if config is None: config = HTTPSConfig.get_config() # Base configuration email = "admin@localhost" if config and config.email: email = config.email base_config = f"""{{ # Global options email {email} # Admin API for configuration management (listen on all interfaces) admin 0.0.0.0:2019 # Uncomment for testing to avoid rate limits # acme_ca https://acme-staging-v02.api.letsencrypt.org/directory }} # Shared reverse proxy configuration (reverse_proxy_config) {{ reverse_proxy digiserver-app:5000 {{ header_up Host {{host}} header_up X-Real-IP {{remote_host}} header_up X-Forwarded-Proto {{scheme}} # Timeouts for large uploads transport http {{ read_timeout 300s write_timeout 300s }} }} # File upload size limit (2GB) request_body {{ max_size 2GB }} # Security headers header {{ X-Frame-Options "SAMEORIGIN" X-Content-Type-Options "nosniff" X-XSS-Protection "1; mode=block" }} # Logging log {{ output file /var/log/caddy/access.log }} }} # Localhost (development/local access) http://localhost {{ import reverse_proxy_config }} """ # Add main domain/IP configuration if HTTPS is enabled if config and config.https_enabled and config.domain and config.ip_address: # Internal domain configuration domain_config = f""" # Internal domain (HTTP only - internal use) http://{config.domain} {{ import reverse_proxy_config }} # Handle IP address access http://{config.ip_address} {{ import reverse_proxy_config }} """ base_config += domain_config else: # Default fallback configuration base_config += """ # Internal domain (HTTP only - internal use) http://digiserver.sibiusb.harting.intra { import reverse_proxy_config } # Handle IP address access http://10.76.152.164 { import reverse_proxy_config } """ # Add catch-all for any other HTTP requests base_config += """ # Catch-all for any other HTTP requests http://* { import reverse_proxy_config } """ return base_config @staticmethod def write_caddyfile(caddyfile_content: str, path: str = '/app/Caddyfile') -> bool: """Write Caddyfile to disk. Args: caddyfile_content: Content to write path: Path to Caddyfile Returns: True if successful, False otherwise """ try: with open(path, 'w') as f: f.write(caddyfile_content) return True except Exception as e: print(f"Error writing Caddyfile: {str(e)}") return False @staticmethod def reload_caddy() -> bool: """Reload Caddy configuration without restart. Note: Caddy monitoring is handled via file watching. After writing the Caddyfile, Caddy should automatically reload. If it doesn't, you may need to restart the Caddy container manually. Returns: True if configuration was written successfully (Caddy will auto-reload) """ try: # Just verify that Caddy is reachable import urllib.request response = urllib.request.urlopen('http://caddy:2019/config/', timeout=2) return response.status == 200 except Exception as e: # Caddy might not be reachable, but Caddyfile was already written # Caddy should reload automatically when it detects file changes print(f"Note: Caddy reload check returned: {str(e)}") return True # Return True anyway since Caddyfile was written