shakauthossain commited on
Commit
4e9eb6a
·
0 Parent(s):

Version 1.0.0

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. .flake8 +11 -0
  2. .github/workflows/ci.yml +51 -0
  3. .github/workflows/docker.yml +38 -0
  4. .gitignore +151 -0
  5. Dockerfile +27 -0
  6. Makefile +48 -0
  7. Procfile +1 -0
  8. README.md +44 -0
  9. app/__init__.py +3 -0
  10. app/api/__init__.py +0 -0
  11. app/api/deps.py +41 -0
  12. app/api/routes/__init__.py +0 -0
  13. app/api/routes/nginx.py +66 -0
  14. app/api/routes/scripts.py +79 -0
  15. app/core/__init__.py +0 -0
  16. app/core/config.py +39 -0
  17. app/core/logging.py +41 -0
  18. app/core/security.py +13 -0
  19. app/main.py +112 -0
  20. app/models/__init__.py +0 -0
  21. app/models/schemas.py +195 -0
  22. app/services/__init__.py +0 -0
  23. app/services/devops.py +203 -0
  24. app/templates/angular_setup.j2 +10 -0
  25. app/templates/apache_setup.j2 +18 -0
  26. app/templates/auto_updates_setup.j2 +8 -0
  27. app/templates/backups_setup.j2 +5 -0
  28. app/templates/caddy_setup.j2 +20 -0
  29. app/templates/cherokee_setup.j2 +8 -0
  30. app/templates/disable_root_setup.j2 +10 -0
  31. app/templates/docker_compose_setup.j2 +9 -0
  32. app/templates/docker_setup.j2 +9 -0
  33. app/templates/dotnet_setup.j2 +5 -0
  34. app/templates/fail2ban_setup.j2 +8 -0
  35. app/templates/go_setup.j2 +5 -0
  36. app/templates/haproxy_setup.j2 +38 -0
  37. app/templates/java_setup.j2 +5 -0
  38. app/templates/litespeed_setup.j2 +13 -0
  39. app/templates/mariadb_setup.j2 +16 -0
  40. app/templates/mongodb_setup.j2 +19 -0
  41. app/templates/monitoring_setup.j2 +7 -0
  42. app/templates/mysql_setup.j2 +16 -0
  43. app/templates/nginx/nginx_docker.conf.j2 +13 -0
  44. app/templates/nginx/nginx_dotnet.conf.j2 +13 -0
  45. app/templates/nginx/nginx_go.conf.j2 +13 -0
  46. app/templates/nginx/nginx_java.conf.j2 +13 -0
  47. app/templates/nginx/nginx_nodejs.conf.j2 +13 -0
  48. app/templates/nginx/nginx_php.conf.j2 +18 -0
  49. app/templates/nginx/nginx_python.conf.j2 +13 -0
  50. app/templates/nginx/nginx_static.conf.j2 +11 -0
.flake8 ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [flake8]
2
+ max-line-length = 88
3
+ extend-ignore = E203,W503
4
+ exclude =
5
+ .git,
6
+ __pycache__,
7
+ build,
8
+ dist,
9
+ *.egg-info,
10
+ .venv,
11
+ venv
.github/workflows/ci.yml ADDED
@@ -0,0 +1,51 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ name: Backend CI
2
+
3
+ on:
4
+ push:
5
+ branches: [ main, develop ]
6
+ pull_request:
7
+ branches: [ main ]
8
+
9
+ jobs:
10
+ backend:
11
+ runs-on: ubuntu-latest
12
+ strategy:
13
+ matrix:
14
+ python-version: ["3.9", "3.10", "3.11"]
15
+ steps:
16
+ - uses: actions/checkout@v4
17
+ - name: Setup Python ${{ matrix.python-version }}
18
+ uses: actions/setup-python@v4
19
+ with:
20
+ python-version: ${{ matrix.python-version }}
21
+ - name: Cache pip
22
+ uses: actions/cache@v3
23
+ with:
24
+ path: ~/.cache/pip
25
+ key: ${{ runner.os }}-pip-${{ hashFiles('backend/pyproject.toml', 'backend/requirements.txt') }}
26
+ restore-keys: |
27
+ ${{ runner.os }}-pip-
28
+ - name: Install dependencies
29
+ run: |
30
+ cd backend
31
+ python -m pip install --upgrade pip
32
+ pip install -e ".[dev]"
33
+ - name: Lint with flake8
34
+ run: |
35
+ cd backend
36
+ flake8 app/ tests/
37
+ - name: Type check with mypy
38
+ run: |
39
+ cd backend
40
+ mypy app/
41
+ - name: Run tests with coverage
42
+ run: |
43
+ cd backend
44
+ python -m pytest --cov --cov-report=xml --cov-report=term-missing
45
+ - name: Upload coverage to Codecov
46
+ uses: codecov/codecov-action@v3
47
+ with:
48
+ file: ./backend/coverage.xml
49
+ flags: unittests
50
+ name: codecov-umbrella
51
+ fail_ci_if_error: false
.github/workflows/docker.yml ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ name: Docker Build (Backend)
2
+
3
+ on:
4
+ push:
5
+ branches: [ main ]
6
+ tags: [ 'v*' ]
7
+
8
+ jobs:
9
+ build-and-push-backend:
10
+ runs-on: ubuntu-latest
11
+ steps:
12
+ - name: Checkout
13
+ uses: actions/checkout@v4
14
+ - name: Set up Docker Buildx
15
+ uses: docker/setup-buildx-action@v3
16
+ - name: Log in to Docker Hub
17
+ uses: docker/login-action@v3
18
+ with:
19
+ username: ${{ secrets.DOCKER_USERNAME }}
20
+ password: ${{ secrets.DOCKER_PASSWORD }}
21
+ - name: Extract metadata
22
+ id: meta
23
+ uses: docker/metadata-action@v5
24
+ with:
25
+ images: yourusername/deploymate-backend
26
+ tags: |
27
+ type=ref,event=branch
28
+ type=ref,event=pr
29
+ type=semver,pattern={{version}}
30
+ type=semver,pattern={{major}}.{{minor}}
31
+ - name: Build and push backend
32
+ uses: docker/build-push-action@v5
33
+ with:
34
+ context: ./backend
35
+ file: ./backend/Dockerfile
36
+ push: true
37
+ tags: ${{ steps.meta.outputs.tags }}
38
+ labels: ${{ steps.meta.outputs.labels }}
.gitignore ADDED
@@ -0,0 +1,151 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+
6
+ # C extensions
7
+ *.so
8
+
9
+ # Distribution / packaging
10
+ .Python
11
+ build/
12
+ develop-eggs/
13
+ dist/
14
+ downloads/
15
+ eggs/
16
+ .eggs/
17
+ lib/
18
+ lib64/
19
+ parts/
20
+ sdist/
21
+ var/
22
+ wheels/
23
+ pip-wheel-metadata/
24
+ share/python-wheels/
25
+ *.egg-info/
26
+ .installed.cfg
27
+ *.egg
28
+ MANIFEST
29
+
30
+ # PyInstaller
31
+ # Usually these files are written by a python script from a template
32
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
33
+ *.manifest
34
+ *.spec
35
+
36
+ # Installer logs
37
+ pip-log.txt
38
+ pip-delete-this-directory.txt
39
+
40
+ # Unit test / coverage reports
41
+ htmlcov/
42
+ .tox/
43
+ .nox/
44
+ .coverage
45
+ .coverage.*
46
+ .cache
47
+ nosetests.xml
48
+ coverage.xml
49
+ *.cover
50
+ *.py,cover
51
+ .hypothesis/
52
+ .pytest_cache/
53
+
54
+ # Translations
55
+ *.mo
56
+ *.pot
57
+
58
+ # Django stuff:
59
+ *.log
60
+ local_settings.py
61
+ db.sqlite3
62
+ db.sqlite3-journal
63
+
64
+ # Flask stuff:
65
+ instance/
66
+ .webassets-cache
67
+
68
+ # Scrapy stuff:
69
+ .scrapy
70
+
71
+ # Sphinx documentation
72
+ docs/_build/
73
+
74
+ # PyBuilder
75
+ target/
76
+
77
+ # Jupyter Notebook
78
+ .ipynb_checkpoints
79
+
80
+ # IPython
81
+ profile_default/
82
+ ipython_config.py
83
+
84
+ # pyenv
85
+ .python-version
86
+
87
+ # pipenv
88
+ # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
89
+ # However, in case of collaboration, if having platform-specific dependencies or dependencies
90
+ # having no cross-platform support, pipenv may install dependencies that don't work, or not
91
+ # install all needed dependencies.
92
+ #Pipfile.lock
93
+
94
+ # PEP 582; used by e.g. github.com/David-OConnor/pyflow
95
+ __pypackages__/
96
+
97
+ # Celery stuff
98
+ celerybeat-schedule
99
+ celerybeat.pid
100
+
101
+ # SageMath parsed files
102
+ *.sage.py
103
+
104
+ # Environments
105
+ .env
106
+ .venv
107
+ env/
108
+ venv/
109
+ ENV/
110
+ env.bak/
111
+ venv.bak/
112
+
113
+ # Spyder project settings
114
+ .spyderproject
115
+ .spyproject
116
+
117
+ # Rope project settings
118
+ .ropeproject
119
+
120
+ # mkdocs documentation
121
+ /site
122
+
123
+ # mypy
124
+ .mypy_cache/
125
+ .dmypy.json
126
+ dmypy.json
127
+
128
+ # Pyre type checker
129
+ .pyre/
130
+
131
+ # Application specific
132
+ logs/
133
+ generated/
134
+ *.log
135
+ server.log
136
+
137
+ # IDE
138
+ .vscode/
139
+ .idea/
140
+ *.swp
141
+ *.swo
142
+ *~
143
+
144
+ # OS
145
+ .DS_Store
146
+ .DS_Store?
147
+ ._*
148
+ .Spotlight-V100
149
+ .Trashes
150
+ ehthumbs.db
151
+ Thumbs.db
Dockerfile ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Backend Dockerfile
2
+ FROM python:3.11-slim
3
+
4
+ WORKDIR /app
5
+
6
+ # Install system dependencies
7
+ RUN apt-get update && apt-get install -y \
8
+ gcc \
9
+ && rm -rf /var/lib/apt/lists/*
10
+
11
+ # Copy requirements and install Python dependencies
12
+ COPY requirements.txt .
13
+ RUN pip install --no-cache-dir -r requirements.txt
14
+
15
+ # Copy application code
16
+ COPY . .
17
+
18
+ # Create non-root user
19
+ RUN useradd --create-home --shell /bin/bash app \
20
+ && chown -R app:app /app
21
+ USER app
22
+
23
+ # Expose port
24
+ EXPOSE 8000
25
+
26
+ # Run the application
27
+ CMD ["python", "main.py"]
Makefile ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # DevOps Toolkit - Backend Makefile
2
+
3
+ .PHONY: help install install-dev test test-cov lint type-check clean run format check all
4
+
5
+ help: ## Show this help message
6
+ @echo "Available commands:"
7
+ @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf " %-15s %s\n", $$1, $$2}'
8
+
9
+ install: ## Install production dependencies
10
+ pip install -e .
11
+
12
+ install-dev: ## Install development dependencies
13
+ pip install -e ".[dev]"
14
+
15
+ test: ## Run tests
16
+ python -m pytest
17
+
18
+ test-cov: ## Run tests with coverage
19
+ python -m pytest --cov --cov-report=term-missing --cov-report=html
20
+
21
+ lint: ## Run linting
22
+ flake8 app/ tests/
23
+
24
+ type-check: ## Run type checking
25
+ mypy app/
26
+
27
+ format: ## Format code with black and isort
28
+ black app/ tests/
29
+ isort app/ tests/
30
+
31
+ check: ## Run all checks (lint, type-check, test)
32
+ $(MAKE) lint
33
+ $(MAKE) type-check
34
+ $(MAKE) test
35
+
36
+ clean: ## Clean up generated files
37
+ find . -type f -name "*.pyc" -delete
38
+ find . -type d -name "__pycache__" -delete
39
+ find . -type d -name "*.egg-info" -exec rm -rf {} +
40
+ rm -rf .coverage htmlcov/ .pytest_cache/ .mypy_cache/
41
+
42
+ run: ## Run the application
43
+ python run.py
44
+
45
+ run-dev: ## Run the application in development mode with auto-reload
46
+ uvicorn app.main:app --reload --host 0.0.0.0 --port 8000
47
+
48
+ all: install-dev check ## Install dev dependencies and run all checks
Procfile ADDED
@@ -0,0 +1 @@
 
 
1
+ web: python main.py
README.md ADDED
@@ -0,0 +1,44 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # DeployMate Backend
2
+
3
+ This is the backend service for DeployMate, built with FastAPI.
4
+
5
+ ## Features
6
+ - RESTful API with FastAPI
7
+ - Automated testing, linting, and type checking via GitHub Actions
8
+ - Docker support for containerized deployment
9
+
10
+ ## Development
11
+
12
+ ### Setup
13
+ 1. Create and activate a virtual environment:
14
+ ```bash
15
+ python3 -m venv venv
16
+ source venv/bin/activate
17
+ ```
18
+ 2. Install dependencies:
19
+ ```bash
20
+ pip install -e .[dev]
21
+ ```
22
+ 3. Run the development server:
23
+ ```bash
24
+ uvicorn app.main:app --reload
25
+ ```
26
+
27
+ ### Testing
28
+ Run all tests:
29
+ ```bash
30
+ pytest
31
+ ```
32
+
33
+ ### Linting & Type Checking
34
+ ```bash
35
+ flake8 app/ tests/
36
+ mypy app/
37
+ ```
38
+
39
+ ### Docker
40
+ Build and run with Docker:
41
+ ```bash
42
+ docker build -t deploymate-backend .
43
+ docker run -p 8000:8000 deploymate-backend
44
+ ```
app/__init__.py ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ """
2
+ DevOps Toolkit API - FastAPI app for generating VPS setup scripts and nginx configs.
3
+ """
app/api/__init__.py ADDED
File without changes
app/api/deps.py ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Dependency injection functions for the DevOps Toolkit API.
3
+ """
4
+
5
+ import jinja2
6
+
7
+ from app.core.config import settings
8
+ from app.core.logging import logger
9
+ from app.services.devops import DevOpsService
10
+
11
+ # Jinja2 environment for template rendering
12
+ template_env = jinja2.Environment(
13
+ loader=jinja2.FileSystemLoader(settings.templates_dir),
14
+ trim_blocks=True,
15
+ lstrip_blocks=True,
16
+ autoescape=True,
17
+ cache_size=0, # Disable template caching to allow live reloading
18
+ )
19
+
20
+ # Service instance
21
+ devops_service = DevOpsService(template_env, settings, logger)
22
+
23
+
24
+ def get_settings():
25
+ """Get application settings."""
26
+ return settings
27
+
28
+
29
+ def get_template_env() -> jinja2.Environment:
30
+ """Get Jinja2 template environment."""
31
+ return template_env
32
+
33
+
34
+ def get_logger():
35
+ """Get application logger."""
36
+ return logger
37
+
38
+
39
+ def get_devops_service() -> DevOpsService:
40
+ """Get DevOps service instance."""
41
+ return devops_service
app/api/routes/__init__.py ADDED
File without changes
app/api/routes/nginx.py ADDED
@@ -0,0 +1,66 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ API routes for nginx configuration generation.
3
+ """
4
+
5
+ import uuid
6
+
7
+ from fastapi import APIRouter, Depends, HTTPException, Request, status
8
+
9
+ from app.api.deps import get_devops_service, get_logger, get_settings
10
+ from app.core.security import limiter
11
+ from app.models.schemas import ConfigState, GeneratedFiles
12
+
13
+ router = APIRouter()
14
+
15
+
16
+ @router.post("/generate-nginx-config", response_model=GeneratedFiles)
17
+ @limiter.limit("10/minute")
18
+ async def generate_nginx_config(
19
+ request: Request,
20
+ config: ConfigState,
21
+ service=Depends(get_devops_service),
22
+ settings=Depends(get_settings),
23
+ logger=Depends(get_logger),
24
+ ):
25
+ """Generate nginx configuration based on stack type."""
26
+ client_ip = request.client.host if request.client else "unknown"
27
+ logger.info(
28
+ f"Nginx config generation request from {client_ip} for {config.stackType}"
29
+ )
30
+
31
+ try:
32
+ # Generate unique filename
33
+ script_id = str(uuid.uuid4())[:8]
34
+
35
+ # Generate nginx config
36
+ nginx_config = service.generate_nginx_config(config, script_id)
37
+ nginx_filename = f"nginx-{script_id}.conf"
38
+ nginx_path = settings.generated_dir / nginx_filename
39
+
40
+ # Ensure generated directory exists
41
+ nginx_path.parent.mkdir(exist_ok=True)
42
+
43
+ # Write nginx config file
44
+ with open(nginx_path, "w") as f:
45
+ f.write(nginx_config)
46
+
47
+ # Create nginx config URL for frontend
48
+ nginx_url = f"/download/{nginx_filename}"
49
+
50
+ response = GeneratedFiles(
51
+ script_path="",
52
+ script_url="",
53
+ nginx_config_path=str(nginx_path),
54
+ nginx_url=nginx_url,
55
+ description="Nginx Configuration for " + config.stackType + " + " +
56
+ (config.domain or 'your-domain.com'),
57
+ )
58
+
59
+ return response
60
+
61
+ except Exception as e:
62
+ logger.error(f"Failed to generate nginx config: {str(e)}")
63
+ raise HTTPException(
64
+ status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
65
+ detail=f"Failed to generate nginx config: {str(e)}",
66
+ )
app/api/routes/scripts.py ADDED
@@ -0,0 +1,79 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ API routes for script generation.
3
+ """
4
+
5
+ import os
6
+ import uuid
7
+
8
+ from fastapi import APIRouter, Depends, HTTPException, Request, status
9
+
10
+ from app.api.deps import get_devops_service, get_logger, get_settings
11
+ from app.core.security import limiter
12
+ from app.models.schemas import ConfigState, GeneratedFiles
13
+
14
+ router = APIRouter()
15
+
16
+
17
+ @router.post("/generate-script", response_model=GeneratedFiles)
18
+ @limiter.limit("10/minute")
19
+ async def generate_script(
20
+ request: Request,
21
+ config: ConfigState,
22
+ service=Depends(get_devops_service),
23
+ settings=Depends(get_settings),
24
+ logger=Depends(get_logger),
25
+ ):
26
+ """Generate a VPS setup script based on configuration."""
27
+ client_ip = request.client.host if request.client else "unknown"
28
+ logger.info(f"Script generation request from {client_ip} for {config.stackType}")
29
+
30
+ try:
31
+ # Generate unique filename
32
+ script_id = str(uuid.uuid4())[:8]
33
+
34
+ # Generate bash script
35
+ script_content = service.generate_bash_script(config, script_id)
36
+ script_filename = f"setup-{script_id}.sh"
37
+ script_path = settings.generated_dir / script_filename
38
+
39
+ # Ensure generated directory exists
40
+ script_path.parent.mkdir(exist_ok=True)
41
+
42
+ # Write script file
43
+ with open(script_path, "w") as f:
44
+ f.write(script_content)
45
+
46
+ # Make script executable
47
+ os.chmod(script_path, 0o755)
48
+
49
+ # Create script URL for frontend
50
+ script_url = f"/download/{script_filename}"
51
+
52
+ response = GeneratedFiles(
53
+ script_path=str(script_path),
54
+ script_url=script_url,
55
+ description="VPS Setup Script for " + config.stackType +
56
+ " + " + config.webServer + " + " +
57
+ (config.database or "No Database"),
58
+ )
59
+
60
+ # Generate nginx config if web server is selected
61
+ if config.webServer and config.webServer != "none":
62
+ nginx_config = service.generate_nginx_config(config, script_id)
63
+ nginx_filename = f"nginx-{script_id}.conf"
64
+ nginx_path = settings.generated_dir / nginx_filename
65
+
66
+ with open(nginx_path, "w") as f:
67
+ f.write(nginx_config)
68
+
69
+ response.nginx_config_path = str(nginx_path)
70
+ response.nginx_url = f"/download/{nginx_filename}"
71
+
72
+ return response
73
+
74
+ except Exception as e:
75
+ logger.error(f"Failed to generate script: {str(e)}")
76
+ raise HTTPException(
77
+ status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
78
+ detail=f"Failed to generate script: {str(e)}",
79
+ )
app/core/__init__.py ADDED
File without changes
app/core/config.py ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Core configuration for the DevOps Toolkit API.
3
+ """
4
+
5
+ import os
6
+ from pathlib import Path
7
+ from typing import List
8
+
9
+
10
+ class Settings:
11
+ """Application settings loaded from environment variables."""
12
+
13
+ def __init__(self):
14
+ # Server settings
15
+ self.host: str = os.getenv("HOST", "0.0.0.0")
16
+ self.port: int = int(os.getenv("PORT", 8000))
17
+ self.debug: bool = os.getenv("DEBUG", "false").lower() == "true"
18
+
19
+ # Security settings
20
+ self.allowed_origins: List[str] = os.getenv("ALLOWED_ORIGINS", "*").split(",")
21
+ self.rate_limit_requests: int = int(os.getenv("RATE_LIMIT_REQUESTS", 10))
22
+
23
+ # Logging settings
24
+ self.log_level: str = os.getenv("LOG_LEVEL", "INFO")
25
+
26
+ # Path settings
27
+ self.base_dir: Path = Path(__file__).parent.parent
28
+ self.templates_dir: Path = self.base_dir / "templates"
29
+ self.generated_dir: Path = self.base_dir.parent / "generated"
30
+ self.logs_dir: Path = self.base_dir.parent / "logs"
31
+
32
+ # Ensure directories exist
33
+ self.templates_dir.mkdir(exist_ok=True)
34
+ self.generated_dir.mkdir(exist_ok=True)
35
+ self.logs_dir.mkdir(exist_ok=True)
36
+
37
+
38
+ # Global settings instance
39
+ settings = Settings()
app/core/logging.py ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Logging configuration for the DevOps Toolkit API.
3
+ """
4
+
5
+ import sys
6
+
7
+ from loguru import logger as loguru_logger
8
+
9
+ from app.core.config import settings
10
+
11
+
12
+ def setup_logging():
13
+ """Configure application logging."""
14
+ # Remove default handler
15
+ loguru_logger.remove()
16
+
17
+ # Add file handler with rotation and retention
18
+ loguru_logger.add(
19
+ settings.logs_dir / "devops_toolkit.log",
20
+ rotation="10 MB",
21
+ retention="1 week",
22
+ level=settings.log_level,
23
+ format="{time:YYYY-MM-DD HH:mm:ss} | {level} | "
24
+ "{name}:{function}:{line} | {message}",
25
+ backtrace=True,
26
+ diagnose=True,
27
+ )
28
+
29
+ # Add console handler
30
+ loguru_logger.add(
31
+ sys.stdout,
32
+ level=settings.log_level,
33
+ format="{time:YYYY-MM-DD HH:mm:ss} | {level} | {message}",
34
+ colorize=True,
35
+ )
36
+
37
+ return loguru_logger
38
+
39
+
40
+ # Global logger instance
41
+ logger = setup_logging()
app/core/security.py ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Security utilities for the DevOps Toolkit API.
3
+ """
4
+
5
+ from slowapi import Limiter
6
+ from slowapi.middleware import SlowAPIMiddleware
7
+ from slowapi.util import get_remote_address
8
+
9
+ # Rate limiter instance
10
+ limiter = Limiter(key_func=get_remote_address)
11
+
12
+ # Rate limiting middleware
13
+ rate_limit_middleware = SlowAPIMiddleware
app/main.py ADDED
@@ -0,0 +1,112 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Main FastAPI application for DeployMate.
3
+ """
4
+
5
+ from contextlib import asynccontextmanager
6
+
7
+ from fastapi import Depends, FastAPI, HTTPException, Request
8
+ from fastapi.exceptions import RequestValidationError
9
+ from fastapi.middleware.cors import CORSMiddleware
10
+ from fastapi.responses import FileResponse, JSONResponse
11
+ from slowapi import _rate_limit_exceeded_handler
12
+ from slowapi.errors import RateLimitExceeded
13
+
14
+ from app.api.deps import get_logger, get_settings
15
+ from app.api.routes.nginx import router as nginx_router
16
+ from app.api.routes.scripts import router as scripts_router
17
+ from app.core.config import settings
18
+ from app.core.logging import logger
19
+ from app.core.security import limiter, rate_limit_middleware
20
+
21
+
22
+ @asynccontextmanager
23
+ async def lifespan(app: FastAPI):
24
+ """Application lifespan context manager."""
25
+ logger.info("Starting DeployMate API")
26
+ yield
27
+ logger.info("Shutting down DeployMate API")
28
+
29
+
30
+ # Create FastAPI application
31
+ app = FastAPI(
32
+ title="DeployMate API",
33
+ version="1.0.0",
34
+ description="API for generating VPS setup scripts and nginx configurations",
35
+ lifespan=lifespan,
36
+ )
37
+
38
+ # Rate limiting
39
+ app.state.limiter = limiter
40
+ app.add_exception_handler(RateLimitExceeded, _rate_limit_exceeded_handler)
41
+ app.add_middleware(rate_limit_middleware)
42
+
43
+ # CORS middleware
44
+ app.add_middleware(
45
+ CORSMiddleware,
46
+ allow_origins=settings.allowed_origins,
47
+ allow_credentials=True,
48
+ allow_methods=["*"],
49
+ allow_headers=["*"],
50
+ )
51
+
52
+
53
+ # Exception handlers
54
+ @app.exception_handler(RequestValidationError)
55
+ async def validation_exception_handler(request: Request, exc: RequestValidationError):
56
+ """Handle Pydantic validation errors."""
57
+ client_ip = request.client.host if request.client else "unknown"
58
+ errors = []
59
+ for error in exc.errors():
60
+ errors.append(
61
+ {
62
+ "type": error.get("type"),
63
+ "loc": error.get("loc"),
64
+ "msg": error.get("msg"),
65
+ "input": error.get("input"),
66
+ }
67
+ )
68
+ logger.error(f"Validation error from {client_ip}: {errors}")
69
+ return JSONResponse(
70
+ status_code=422,
71
+ content={"detail": errors},
72
+ )
73
+
74
+
75
+ # Root endpoint
76
+ @app.get("/")
77
+ async def root():
78
+ """Root endpoint returning API information."""
79
+ return {
80
+ "message": "DeployMate API",
81
+ "version": "1.0.0",
82
+ "docs": "/docs",
83
+ "redoc": "/redoc",
84
+ }
85
+
86
+
87
+ # File download endpoint
88
+ @app.get("/download/{filename}")
89
+ async def download_file(
90
+ filename: str,
91
+ request: Request,
92
+ settings=Depends(get_settings),
93
+ logger=Depends(get_logger)
94
+ ):
95
+ """Download a generated file."""
96
+ client_ip = request.client.host if request.client else "unknown"
97
+ logger.info(f"File download request from {client_ip}: {filename}")
98
+
99
+ file_path = settings.generated_dir / filename
100
+ if not file_path.exists():
101
+ logger.warning(f"File not found: {filename}")
102
+ raise HTTPException(status_code=404, detail="File not found")
103
+
104
+ logger.info(f"Serving file: {filename}")
105
+ return FileResponse(
106
+ path=file_path, filename=filename, media_type="application/octet-stream"
107
+ )
108
+
109
+
110
+ # Include API routers
111
+ app.include_router(scripts_router, prefix="/api/v1", tags=["scripts"])
112
+ app.include_router(nginx_router, prefix="/api/v1", tags=["nginx"])
app/models/__init__.py ADDED
File without changes
app/models/schemas.py ADDED
@@ -0,0 +1,195 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Pydantic models and schemas for the DevOps Toolkit API.
3
+ """
4
+
5
+ import re
6
+ from typing import Optional
7
+
8
+ from pydantic import BaseModel, field_validator, model_validator
9
+
10
+
11
+ class SecurityConfig(BaseModel):
12
+ """Security configuration options."""
13
+
14
+ ufw: bool = True
15
+ fail2ban: bool = True
16
+ disableRoot: bool = True
17
+ autoUpdates: bool = True
18
+
19
+
20
+ class AdvancedConfig(BaseModel):
21
+ """Advanced configuration options."""
22
+
23
+ swap: bool = False
24
+ swapSize: str = "2G"
25
+ sshKey: str = ""
26
+ backups: bool = False
27
+ monitoring: bool = False
28
+
29
+
30
+ class ConfigState(BaseModel):
31
+ """Main configuration state for script generation."""
32
+
33
+ stackType: str # nodejs, python, php, static
34
+ webServer: str # nginx, apache
35
+ database: str # postgresql, mysql, mongodb, none
36
+ dbName: str = ""
37
+ dbUsername: str = ""
38
+ dbPassword: str = ""
39
+ domain: Optional[str] = None
40
+ ssl: bool = False
41
+ sslEmail: str = ""
42
+ runtime: str = "" # 20, 18, 16 for nodejs; 3.11, 3.10 for python; 8.3, 8.2 for php
43
+ processManager: str = "" # pm2, systemd, supervisor
44
+ security: SecurityConfig = SecurityConfig()
45
+ advanced: AdvancedConfig = AdvancedConfig()
46
+
47
+ @field_validator("stackType")
48
+ @classmethod
49
+ def validate_stack_type(cls, v):
50
+ allowed = [
51
+ "nodejs",
52
+ "python",
53
+ "php",
54
+ "static",
55
+ "docker",
56
+ "react",
57
+ "vue",
58
+ "angular",
59
+ "go",
60
+ "java",
61
+ "dotnet",
62
+ ]
63
+ if v not in allowed:
64
+ raise ValueError(
65
+ f'Invalid stack type. Must be one of: {", ".join(allowed)}'
66
+ )
67
+ return v
68
+
69
+ @field_validator("webServer")
70
+ @classmethod
71
+ def validate_web_server(cls, v):
72
+ if v is None or v == "":
73
+ return v
74
+ allowed = [
75
+ "nginx",
76
+ "apache",
77
+ "caddy",
78
+ "litespeed",
79
+ "tomcat",
80
+ "cherokee",
81
+ "haproxy",
82
+ "none",
83
+ ]
84
+ if v and v not in allowed:
85
+ raise ValueError(
86
+ f'Invalid web server. Must be one of: {", ".join(allowed)}'
87
+ )
88
+ return v
89
+
90
+ @field_validator("database")
91
+ @classmethod
92
+ def validate_database(cls, v):
93
+ allowed = [
94
+ "postgresql",
95
+ "mysql",
96
+ "mariadb",
97
+ "mongodb",
98
+ "redis",
99
+ "cassandra",
100
+ "couchdb",
101
+ "sqlite",
102
+ "elasticsearch",
103
+ "none",
104
+ ]
105
+ if v and v not in allowed:
106
+ raise ValueError(f'Invalid database. Must be one of: {", ".join(allowed)}')
107
+ return v
108
+
109
+ @field_validator("processManager")
110
+ @classmethod
111
+ def validate_process_manager(cls, v):
112
+ allowed = ["pm2", "systemd", "supervisor", "docker-compose", "none"]
113
+ if v and v not in allowed:
114
+ raise ValueError(
115
+ f'Invalid process manager. Must be one of: {", ".join(allowed)}'
116
+ )
117
+ return v
118
+
119
+ @field_validator("domain")
120
+ @classmethod
121
+ def validate_domain(cls, v):
122
+ if v and not re.match(
123
+ r"^(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}$", v
124
+ ):
125
+ raise ValueError("Invalid domain format")
126
+ return v
127
+
128
+ @model_validator(mode="after")
129
+ def validate_database_credentials(self):
130
+ # Databases that require credentials
131
+ dbs_requiring_creds = ["postgresql", "mysql", "mariadb", "mongodb"]
132
+
133
+ if self.database in dbs_requiring_creds:
134
+ if not self.dbName or self.dbName.strip() == "":
135
+ raise ValueError("dbName is required for " + self.database)
136
+ if not self.dbUsername or self.dbUsername.strip() == "":
137
+ raise ValueError("dbUsername is required for " + self.database)
138
+ if not self.dbPassword or self.dbPassword.strip() == "":
139
+ raise ValueError("dbPassword is required for " + self.database)
140
+ if len(self.dbPassword) < 8:
141
+ raise ValueError(
142
+ "Password must be at least 8 characters for database access"
143
+ )
144
+
145
+ return self
146
+
147
+
148
+ class GeneratedFiles(BaseModel):
149
+ """Response model for generated files."""
150
+
151
+ script_path: str
152
+ script_url: str
153
+ nginx_config_path: Optional[str] = None
154
+ nginx_url: Optional[str] = None
155
+ description: str
156
+
157
+
158
+ class NginxConfigRequest(BaseModel):
159
+ """Request model for nginx configuration generation."""
160
+
161
+ stackType: str
162
+ domain: str
163
+ ssl: bool = False
164
+ sslEmail: str = ""
165
+
166
+ @field_validator("stackType")
167
+ @classmethod
168
+ def validate_stack_type(cls, v):
169
+ allowed = [
170
+ "nodejs",
171
+ "python",
172
+ "php",
173
+ "static",
174
+ "docker",
175
+ "react",
176
+ "vue",
177
+ "angular",
178
+ "go",
179
+ "java",
180
+ "dotnet",
181
+ ]
182
+ if v not in allowed:
183
+ raise ValueError(
184
+ f'Invalid stack type. Must be one of: {", ".join(allowed)}'
185
+ )
186
+ return v
187
+
188
+ @field_validator("domain")
189
+ @classmethod
190
+ def validate_domain(cls, v):
191
+ if not re.match(
192
+ r"^(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}$", v
193
+ ):
194
+ raise ValueError("Invalid domain format")
195
+ return v
app/services/__init__.py ADDED
File without changes
app/services/devops.py ADDED
@@ -0,0 +1,203 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ DevOps service for script and configuration generation.
3
+ """
4
+
5
+ from typing import Union
6
+
7
+ from fastapi import HTTPException, status
8
+ from jinja2 import Environment
9
+
10
+ from app.core.config import Settings
11
+ from app.models.schemas import ConfigState, NginxConfigRequest
12
+
13
+
14
+ class DevOpsService:
15
+ """Service for generating DevOps scripts and configurations."""
16
+
17
+ def __init__(self, template_env: Environment, settings: Settings, logger):
18
+ self.template_env = template_env
19
+ self.settings = settings
20
+ self.logger = logger
21
+
22
+ def get_script_templates(self, config: ConfigState) -> list[str]:
23
+ """
24
+ Determine which templates to include based on configuration.
25
+ """
26
+ templates = []
27
+
28
+ # Stack type templates
29
+ stack_map = {
30
+ "python": "python_setup.j2",
31
+ "nodejs": "nodejs_setup.j2",
32
+ "php": "php_setup.j2",
33
+ "react": "react_setup.j2",
34
+ "vue": "vue_setup.j2",
35
+ "angular": "angular_setup.j2",
36
+ "go": "go_setup.j2",
37
+ "java": "java_setup.j2",
38
+ "dotnet": "dotnet_setup.j2",
39
+ "docker": "docker_setup.j2",
40
+ }
41
+
42
+ if config.stackType in stack_map:
43
+ templates.append(stack_map[config.stackType])
44
+
45
+ # Web server templates
46
+ web_map = {
47
+ "nginx": "nginx_setup.j2",
48
+ "apache": "apache_setup.j2",
49
+ "caddy": "caddy_setup.j2",
50
+ "litespeed": "litespeed_setup.j2",
51
+ "tomcat": "tomcat_setup.j2",
52
+ "cherokee": "cherokee_setup.j2",
53
+ "haproxy": "haproxy_setup.j2",
54
+ }
55
+
56
+ if config.webServer and config.webServer != "none":
57
+ template_name = web_map.get(config.webServer)
58
+ if template_name:
59
+ templates.append(template_name)
60
+
61
+ # Database templates
62
+ db_map = {
63
+ "postgresql": "postgresql_setup.j2",
64
+ "mysql": "mysql_setup.j2",
65
+ "mariadb": "mariadb_setup.j2",
66
+ "mongodb": "mongodb_setup.j2",
67
+ "redis": "redis_setup.j2",
68
+ }
69
+
70
+ if config.database and config.database != "none":
71
+ template_name = db_map.get(config.database)
72
+ if template_name:
73
+ templates.append(template_name)
74
+
75
+ # Process manager templates
76
+ pm_map = {
77
+ "pm2": "pm2_setup.j2",
78
+ "systemd": "systemd_setup.j2",
79
+ "docker-compose": "docker_compose_setup.j2",
80
+ }
81
+
82
+ if config.processManager and config.processManager != "none":
83
+ template_name = pm_map.get(config.processManager)
84
+ if template_name:
85
+ templates.append(template_name)
86
+
87
+ # Security templates
88
+ if config.security.ufw:
89
+ templates.append("ufw_setup.j2")
90
+ if config.security.fail2ban:
91
+ templates.append("fail2ban_setup.j2")
92
+ if config.security.disableRoot:
93
+ templates.append("disable_root_setup.j2")
94
+ if config.security.autoUpdates:
95
+ templates.append("auto_updates_setup.j2")
96
+
97
+ # Advanced templates
98
+ if config.advanced.swap:
99
+ templates.append("swap_setup.j2")
100
+ if config.advanced.sshKey:
101
+ templates.append("ssh_key_setup.j2")
102
+ if config.advanced.backups:
103
+ templates.append("backups_setup.j2")
104
+ if config.advanced.monitoring:
105
+ templates.append("monitoring_setup.j2")
106
+
107
+ # SSL template
108
+ if config.ssl:
109
+ templates.append("ssl_setup.j2")
110
+
111
+ return templates
112
+
113
+ def generate_bash_script(self, config: ConfigState, script_id: str) -> str:
114
+ """
115
+ Generate a bash script by combining multiple modular templates.
116
+ """
117
+ templates = self.get_script_templates(config)
118
+
119
+ script_parts = []
120
+ for template_name in templates:
121
+ try:
122
+ # Read template directly from disk to avoid caching issues
123
+ template_path = self.settings.templates_dir / template_name
124
+ with open(template_path, 'r', encoding='utf-8') as f:
125
+ template_content = f.read()
126
+
127
+ # Render using Jinja2 Template directly
128
+ from jinja2 import Template
129
+ template = Template(template_content, trim_blocks=True, lstrip_blocks=True)
130
+ part = template.render(config=config, script_id=script_id)
131
+ if part.strip(): # Only add non-empty parts
132
+ script_parts.append(part)
133
+ except Exception as e:
134
+ self.logger.warning(f"Template error for {template_name}: {e}")
135
+
136
+ combined_script = "\n\n".join(script_parts)
137
+
138
+ # Add header
139
+ header = f"""#!/bin/bash
140
+ # Generated DevOps Toolkit Script - {script_id}
141
+ # Stack: {config.stackType}
142
+ # Web Server: {config.webServer or 'None'}
143
+ # Database: {config.database or 'None'}
144
+ # Process Manager: {config.processManager or 'None'}
145
+
146
+ set -e
147
+
148
+ echo "🚀 Starting server setup..."
149
+ """
150
+
151
+ # Add footer
152
+ footer = """
153
+ echo "✅ Setup complete! Your server is ready."
154
+ echo "📝 Next steps:"
155
+ echo " 1. Configure your application"
156
+ echo " 2. Upload your code"
157
+ echo " 3. Start your application service"
158
+ """
159
+
160
+ return header + combined_script + footer
161
+
162
+ def generate_nginx_config(
163
+ self, config: Union[ConfigState, NginxConfigRequest], script_id: str
164
+ ) -> str:
165
+ """
166
+ Generate nginx configuration using modular templates.
167
+ """
168
+ stack = config.stackType
169
+
170
+ # Map stack types to nginx config templates
171
+ nginx_map = {
172
+ "python": "nginx/nginx_python.conf.j2",
173
+ "nodejs": "nginx/nginx_nodejs.conf.j2",
174
+ "php": "nginx/nginx_php.conf.j2",
175
+ "react": "nginx/nginx_static.conf.j2",
176
+ "vue": "nginx/nginx_static.conf.j2",
177
+ "angular": "nginx/nginx_static.conf.j2",
178
+ "go": "nginx/nginx_go.conf.j2",
179
+ "java": "nginx/nginx_java.conf.j2",
180
+ "dotnet": "nginx/nginx_dotnet.conf.j2",
181
+ "docker": "nginx/nginx_docker.conf.j2",
182
+ }
183
+
184
+ template_name = nginx_map.get(
185
+ stack, "nginx/nginx_static.conf.j2"
186
+ ) # Default to static
187
+
188
+ try:
189
+ # Read template directly from disk to avoid caching issues
190
+ template_path = self.settings.templates_dir / template_name
191
+ with open(template_path, 'r', encoding='utf-8') as f:
192
+ template_content = f.read()
193
+
194
+ # Render using Jinja2 Template directly
195
+ from jinja2 import Template
196
+ template = Template(template_content, trim_blocks=True, lstrip_blocks=True)
197
+ return template.render(config=config, script_id=script_id)
198
+ except Exception as e:
199
+ self.logger.error(f"Nginx template error for {template_name}: {e}")
200
+ raise HTTPException(
201
+ status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
202
+ detail=f"Required nginx template {template_name} not found",
203
+ )
app/templates/angular_setup.j2 ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/bin/bash
2
+ # Angular setup script
3
+
4
+ # Install nvm
5
+ curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash
6
+ source ~/.bashrc
7
+
8
+ # Install latest LTS Node.js
9
+ nvm install --lts
10
+ nvm use --lts
app/templates/apache_setup.j2 ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/bin/bash
2
+ # Apache setup script
3
+
4
+ sudo apt-get update
5
+ sudo apt-get install -y apache2
6
+
7
+ # Basic Apache config
8
+ cat > /etc/apache2/sites-available/000-default.conf << 'EOF'
9
+ <VirtualHost *:80>
10
+ DocumentRoot /var/www/html
11
+ <Directory /var/www/html>
12
+ AllowOverride All
13
+ Require all granted
14
+ </Directory>
15
+ </VirtualHost>
16
+ EOF
17
+
18
+ sudo systemctl restart apache2
app/templates/auto_updates_setup.j2 ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ #!/bin/bash
2
+ # Auto updates setup script
3
+
4
+ sudo apt-get update
5
+ sudo apt-get install -y unattended-upgrades
6
+
7
+ # Enable automatic updates
8
+ sudo dpkg-reconfigure --priority=low unattended-upgrades
app/templates/backups_setup.j2 ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ #!/bin/bash
2
+ # Backups setup script
3
+
4
+ # Example: Create a backup of /var/www
5
+ sudo tar -czvf /var/backups/www-backup-$(date +%F).tar.gz /var/www
app/templates/caddy_setup.j2 ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/bin/bash
2
+ # Caddy setup script
3
+
4
+ sudo apt-get update
5
+ sudo apt-get install -y curl
6
+
7
+ # Install Caddy
8
+ curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
9
+ curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.list
10
+ sudo apt-get update
11
+ sudo apt-get install -y caddy
12
+
13
+ # Basic Caddyfile
14
+ cat > /etc/caddy/Caddyfile << 'EOF'
15
+ :80 {
16
+ reverse_proxy localhost:3000
17
+ }
18
+ EOF
19
+
20
+ sudo systemctl restart caddy
app/templates/cherokee_setup.j2 ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ #!/bin/bash
2
+ # Cherokee setup script
3
+
4
+ sudo apt-get update
5
+ sudo apt-get install -y cherokee
6
+
7
+ sudo systemctl enable cherokee
8
+ sudo systemctl start cherokee
app/templates/disable_root_setup.j2 ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/bin/bash
2
+ # Disable root login setup script
3
+
4
+ # Disable root SSH login
5
+ sudo sed -i 's/#PermitRootLogin yes/PermitRootLogin no/' /etc/ssh/sshd_config
6
+ sudo systemctl restart sshd
7
+
8
+ # Create a new user if needed
9
+ # sudo useradd -m -s /bin/bash newuser
10
+ # sudo usermod -aG sudo newuser
app/templates/docker_compose_setup.j2 ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ #!/bin/bash
2
+ # Docker Compose setup script
3
+
4
+ sudo apt-get update
5
+ sudo apt-get install -y docker.io docker-compose
6
+
7
+ # Enable and start Docker
8
+ sudo systemctl enable docker
9
+ sudo systemctl start docker
app/templates/docker_setup.j2 ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ #!/bin/bash
2
+ # Docker setup script
3
+
4
+ sudo apt-get update
5
+ sudo apt-get install -y docker.io docker-compose
6
+
7
+ # Enable and start Docker
8
+ sudo systemctl enable docker
9
+ sudo systemctl start docker
app/templates/dotnet_setup.j2 ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ #!/bin/bash
2
+ # .NET setup script
3
+
4
+ sudo apt-get update
5
+ sudo apt-get install -y dotnet-sdk-7.0
app/templates/fail2ban_setup.j2 ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ #!/bin/bash
2
+ # Fail2Ban setup script
3
+
4
+ sudo apt-get update
5
+ sudo apt-get install -y fail2ban
6
+
7
+ sudo systemctl enable fail2ban
8
+ sudo systemctl start fail2ban
app/templates/go_setup.j2 ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ #!/bin/bash
2
+ # Go setup script
3
+
4
+ sudo apt-get update
5
+ sudo apt-get install -y golang
app/templates/haproxy_setup.j2 ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/bin/bash
2
+ # HAProxy setup script
3
+
4
+ sudo apt-get update
5
+ sudo apt-get install -y haproxy
6
+
7
+ # Basic HAProxy config
8
+ cat > /etc/haproxy/haproxy.cfg << 'EOF'
9
+ global
10
+ log /dev/log local0
11
+ log /dev/log local1 notice
12
+ chroot /var/lib/haproxy
13
+ stats socket /run/haproxy/admin.sock mode 660 level admin expose-fd listeners
14
+ stats timeout 30s
15
+ user haproxy
16
+ group haproxy
17
+ daemon
18
+
19
+ defaults
20
+ log global
21
+ mode http
22
+ option httplog
23
+ option dontlognull
24
+ timeout connect 5000
25
+ timeout client 50000
26
+ timeout server 50000
27
+
28
+ frontend http_front
29
+ bind *:80
30
+ default_backend http_back
31
+
32
+ backend http_back
33
+ balance roundrobin
34
+ server server1 localhost:3000 check
35
+ EOF
36
+
37
+ sudo systemctl enable haproxy
38
+ sudo systemctl start haproxy
app/templates/java_setup.j2 ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ #!/bin/bash
2
+ # Java setup script
3
+
4
+ sudo apt-get update
5
+ sudo apt-get install -y openjdk-17-jdk
app/templates/litespeed_setup.j2 ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/bin/bash
2
+ # LiteSpeed setup script
3
+
4
+ sudo apt-get update
5
+ sudo apt-get install -y wget
6
+
7
+ # Download and install LiteSpeed
8
+ wget -O - http://rpms.litespeedtech.com/debian/enable_lst_debian_repo.sh | sudo bash
9
+ sudo apt-get update
10
+ sudo apt-get install -y openlitespeed
11
+
12
+ sudo systemctl enable lshttpd
13
+ sudo systemctl start lshttpd
app/templates/mariadb_setup.j2 ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/bin/bash
2
+ # MariaDB setup script
3
+
4
+ sudo apt-get update
5
+ sudo apt-get install -y mariadb-server
6
+
7
+ # Secure installation
8
+ sudo mysql_secure_installation
9
+
10
+ # Create database and user
11
+ sudo mysql -u root <<EOF
12
+ CREATE DATABASE mydb;
13
+ CREATE USER 'myuser'@'localhost' IDENTIFIED BY 'mypassword';
14
+ GRANT ALL PRIVILEGES ON mydb.* TO 'myuser'@'localhost';
15
+ FLUSH PRIVILEGES;
16
+ EOF
app/templates/mongodb_setup.j2 ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/bin/bash
2
+ # MongoDB setup script
3
+
4
+ sudo apt-get update
5
+ sudo apt-get install -y mongodb
6
+
7
+ # Start MongoDB
8
+ sudo systemctl enable mongodb
9
+ sudo systemctl start mongodb
10
+
11
+ # Create database and user
12
+ mongo <<EOF
13
+ use mydb
14
+ db.createUser({
15
+ user: "myuser",
16
+ pwd: "mypassword",
17
+ roles: [ { role: "readWrite", db: "mydb" } ]
18
+ })
19
+ EOF
app/templates/monitoring_setup.j2 ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ #!/bin/bash
2
+ # Monitoring setup script
3
+
4
+ sudo apt-get update
5
+ sudo apt-get install -y htop
6
+
7
+ # For more advanced monitoring, consider installing netdata, prometheus, grafana, etc.
app/templates/mysql_setup.j2 ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/bin/bash
2
+ # MySQL setup script
3
+
4
+ sudo apt-get update
5
+ sudo apt-get install -y mysql-server
6
+
7
+ # Secure installation
8
+ sudo mysql_secure_installation
9
+
10
+ # Create database and user
11
+ sudo mysql -u root <<EOF
12
+ CREATE DATABASE mydb;
13
+ CREATE USER 'myuser'@'localhost' IDENTIFIED BY 'mypassword';
14
+ GRANT ALL PRIVILEGES ON mydb.* TO 'myuser'@'localhost';
15
+ FLUSH PRIVILEGES;
16
+ EOF
app/templates/nginx/nginx_docker.conf.j2 ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Nginx config for Docker containers
2
+ server {
3
+ listen 80;
4
+ server_name {{ config.domain or '_' }};
5
+
6
+ location / {
7
+ proxy_pass http://127.0.0.1:{{ config.port or '3000' }};
8
+ proxy_set_header Host $host;
9
+ proxy_set_header X-Real-IP $remote_addr;
10
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
11
+ proxy_set_header X-Forwarded-Proto $scheme;
12
+ }
13
+ }
app/templates/nginx/nginx_dotnet.conf.j2 ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Nginx config for .NET apps
2
+ server {
3
+ listen 80;
4
+ server_name {{ config.domain or '_' }};
5
+
6
+ location / {
7
+ proxy_pass http://127.0.0.1:{{ config.port or '5000' }};
8
+ proxy_set_header Host $host;
9
+ proxy_set_header X-Real-IP $remote_addr;
10
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
11
+ proxy_set_header X-Forwarded-Proto $scheme;
12
+ }
13
+ }
app/templates/nginx/nginx_go.conf.j2 ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Nginx config for Go apps
2
+ server {
3
+ listen 80;
4
+ server_name {{ config.domain or '_' }};
5
+
6
+ location / {
7
+ proxy_pass http://127.0.0.1:{{ config.port or '8080' }};
8
+ proxy_set_header Host $host;
9
+ proxy_set_header X-Real-IP $remote_addr;
10
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
11
+ proxy_set_header X-Forwarded-Proto $scheme;
12
+ }
13
+ }
app/templates/nginx/nginx_java.conf.j2 ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Nginx config for Java apps
2
+ server {
3
+ listen 80;
4
+ server_name {{ config.domain or '_' }};
5
+
6
+ location / {
7
+ proxy_pass http://127.0.0.1:{{ config.port or '8080' }};
8
+ proxy_set_header Host $host;
9
+ proxy_set_header X-Real-IP $remote_addr;
10
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
11
+ proxy_set_header X-Forwarded-Proto $scheme;
12
+ }
13
+ }
app/templates/nginx/nginx_nodejs.conf.j2 ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Nginx config for Node.js apps
2
+ server {
3
+ listen 80;
4
+ server_name {{ config.domain or '_' }};
5
+
6
+ location / {
7
+ proxy_pass http://127.0.0.1:{{ config.port or '3000' }};
8
+ proxy_set_header Host $host;
9
+ proxy_set_header X-Real-IP $remote_addr;
10
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
11
+ proxy_set_header X-Forwarded-Proto $scheme;
12
+ }
13
+ }
app/templates/nginx/nginx_php.conf.j2 ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Nginx config for PHP apps (PHP, Laravel, WordPress)
2
+ server {
3
+ listen 80;
4
+ server_name {{ config.domain or '_' }};
5
+
6
+ root {{ config.dir or '/var/www/laravel/public' }};
7
+ index index.php;
8
+
9
+ location / {
10
+ try_files $uri $uri/ /index.php?$query_string;
11
+ }
12
+
13
+ location ~ \.php$ {
14
+ include fastcgi_params;
15
+ fastcgi_pass unix:/run/php/php8.1-fpm.sock;
16
+ fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
17
+ }
18
+ }
app/templates/nginx/nginx_python.conf.j2 ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Nginx config for Python apps
2
+ server {
3
+ listen 80;
4
+ server_name {{ config.domain or '_' }};
5
+
6
+ location / {
7
+ proxy_pass http://127.0.0.1:{{ config.port or '8000' }};
8
+ proxy_set_header Host $host;
9
+ proxy_set_header X-Real-IP $remote_addr;
10
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
11
+ proxy_set_header X-Forwarded-Proto $scheme;
12
+ }
13
+ }
app/templates/nginx/nginx_static.conf.j2 ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Nginx config for static sites (React, Vue, Angular, etc.)
2
+ server {
3
+ listen 80;
4
+ server_name {{ config.domain or '_' }};
5
+ root /var/www/{{ config.domain or 'site' }}/html;
6
+ index index.html;
7
+
8
+ location / {
9
+ try_files $uri $uri/ /index.html;
10
+ }
11
+ }