deploymate / app /services /devops.py
shakauthossain's picture
V2.0.0
2df0cf9 verified
"""
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",
)