Spaces:
Running
Running
| """ | |
| DevOps service for script and configuration generation. | |
| """ | |
| from typing import Union | |
| from fastapi import HTTPException, status | |
| from jinja2 import Environment | |
| from app.core.config import Settings | |
| from app.models.schemas import ConfigState, NginxConfigRequest | |
| class DevOpsService: | |
| """Service for generating DevOps scripts and configurations.""" | |
| def __init__(self, template_env: Environment, settings: Settings, logger): | |
| self.template_env = template_env | |
| self.settings = settings | |
| self.logger = logger | |
| def get_script_templates(self, config: ConfigState) -> list[str]: | |
| """ | |
| Determine which templates to include based on configuration. | |
| """ | |
| templates = [] | |
| # Stack type templates | |
| stack_map = { | |
| "python": "python_setup.j2", | |
| "nodejs": "nodejs_setup.j2", | |
| "php": "php_setup.j2", | |
| "react": "react_setup.j2", | |
| "vue": "vue_setup.j2", | |
| "angular": "angular_setup.j2", | |
| "go": "go_setup.j2", | |
| "java": "java_setup.j2", | |
| "dotnet": "dotnet_setup.j2", | |
| "docker": "docker_setup.j2", | |
| } | |
| if config.stackType in stack_map: | |
| templates.append(stack_map[config.stackType]) | |
| # Web server templates | |
| web_map = { | |
| "nginx": "nginx_setup.j2", | |
| "apache": "apache_setup.j2", | |
| "caddy": "caddy_setup.j2", | |
| "litespeed": "litespeed_setup.j2", | |
| "tomcat": "tomcat_setup.j2", | |
| "cherokee": "cherokee_setup.j2", | |
| "haproxy": "haproxy_setup.j2", | |
| } | |
| if config.webServer and config.webServer != "none": | |
| template_name = web_map.get(config.webServer) | |
| if template_name: | |
| templates.append(template_name) | |
| # Database templates | |
| db_map = { | |
| "postgresql": "postgresql_setup.j2", | |
| "mysql": "mysql_setup.j2", | |
| "mariadb": "mariadb_setup.j2", | |
| "mongodb": "mongodb_setup.j2", | |
| "redis": "redis_setup.j2", | |
| } | |
| if config.database and config.database != "none": | |
| template_name = db_map.get(config.database) | |
| if template_name: | |
| templates.append(template_name) | |
| # Process manager templates | |
| pm_map = { | |
| "pm2": "pm2_setup.j2", | |
| "systemd": "systemd_setup.j2", | |
| "docker-compose": "docker_compose_setup.j2", | |
| } | |
| if config.processManager and config.processManager != "none": | |
| template_name = pm_map.get(config.processManager) | |
| if template_name: | |
| templates.append(template_name) | |
| # Security templates | |
| if config.security.ufw: | |
| templates.append("ufw_setup.j2") | |
| if config.security.fail2ban: | |
| templates.append("fail2ban_setup.j2") | |
| if config.security.disableRoot: | |
| templates.append("disable_root_setup.j2") | |
| if config.security.autoUpdates: | |
| templates.append("auto_updates_setup.j2") | |
| # Advanced templates | |
| if config.advanced.swap: | |
| templates.append("swap_setup.j2") | |
| if config.advanced.sshKey: | |
| templates.append("ssh_key_setup.j2") | |
| if config.advanced.backups: | |
| templates.append("backups_setup.j2") | |
| if config.advanced.monitoring: | |
| templates.append("monitoring_setup.j2") | |
| # SSL template | |
| if config.ssl: | |
| templates.append("ssl_setup.j2") | |
| return templates | |
| def generate_bash_script(self, config: ConfigState, script_id: str) -> str: | |
| """ | |
| Generate a bash script by combining multiple modular templates. | |
| """ | |
| templates = self.get_script_templates(config) | |
| script_parts = [] | |
| for template_name in templates: | |
| try: | |
| # Read template directly from disk to avoid caching issues | |
| template_path = self.settings.templates_dir / template_name | |
| with open(template_path, 'r', encoding='utf-8') as f: | |
| template_content = f.read() | |
| # Render using Jinja2 Template directly | |
| from jinja2 import Template | |
| template = Template( | |
| template_content, | |
| trim_blocks=True, | |
| lstrip_blocks=True | |
| ) | |
| part = template.render( | |
| config=config, | |
| script_id=script_id | |
| ) | |
| if part.strip(): # Only add non-empty parts | |
| script_parts.append(part) | |
| except Exception as e: | |
| self.logger.warning(f"Template error for {template_name}: {e}") | |
| combined_script = "\n\n".join(script_parts) | |
| # Add header | |
| header = f"""#!/bin/bash | |
| # Generated DevOps Toolkit Script - {script_id} | |
| # Stack: {config.stackType} | |
| # Web Server: {config.webServer or 'None'} | |
| # Database: {config.database or 'None'} | |
| # Process Manager: {config.processManager or 'None'} | |
| set -euo pipefail | |
| echo "π Starting server setup..." | |
| # Update system packages for security | |
| echo "π¦ Updating system packages..." | |
| sudo apt-get update -y | |
| sudo apt-get upgrade -y | |
| """ | |
| # Add footer | |
| footer = """ | |
| echo "β Setup complete! Your server is ready." | |
| echo "π Next steps:" | |
| echo " 1. Configure your application" | |
| echo " 2. Upload your code" | |
| echo " 3. Start your application service" | |
| """ | |
| return header + combined_script + footer | |
| def generate_nginx_config( | |
| self, config: Union[ConfigState, NginxConfigRequest], script_id: str | |
| ) -> str: | |
| """ | |
| Generate nginx configuration using modular templates. | |
| """ | |
| stack = config.stackType | |
| # Map stack types to nginx config templates | |
| nginx_map = { | |
| "python": "nginx/nginx_python.conf.j2", | |
| "nodejs": "nginx/nginx_nodejs.conf.j2", | |
| "php": "nginx/nginx_php.conf.j2", | |
| "react": "nginx/nginx_static.conf.j2", | |
| "vue": "nginx/nginx_static.conf.j2", | |
| "angular": "nginx/nginx_static.conf.j2", | |
| "go": "nginx/nginx_go.conf.j2", | |
| "java": "nginx/nginx_java.conf.j2", | |
| "dotnet": "nginx/nginx_dotnet.conf.j2", | |
| "docker": "nginx/nginx_docker.conf.j2", | |
| } | |
| template_name = nginx_map.get( | |
| stack, "nginx/nginx_static.conf.j2" | |
| ) # Default to static | |
| try: | |
| # Read template directly from disk to avoid caching issues | |
| template_path = self.settings.templates_dir / template_name | |
| with open(template_path, 'r', encoding='utf-8') as f: | |
| template_content = f.read() | |
| # Render using Jinja2 Template directly | |
| from jinja2 import Template | |
| template = Template(template_content, trim_blocks=True, lstrip_blocks=True) | |
| return template.render(config=config, script_id=script_id) | |
| except Exception as e: | |
| self.logger.error(f"Nginx template error for {template_name}: {e}") | |
| raise HTTPException( | |
| status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, | |
| detail=f"Required nginx template {template_name} not found", | |
| ) | |