Spaces:
Running
Running
Commit ·
4e9eb6a
0
Parent(s):
Version 1.0.0
Browse filesThis view is limited to 50 files because it contains too many changes. See raw diff
- .flake8 +11 -0
- .github/workflows/ci.yml +51 -0
- .github/workflows/docker.yml +38 -0
- .gitignore +151 -0
- Dockerfile +27 -0
- Makefile +48 -0
- Procfile +1 -0
- README.md +44 -0
- app/__init__.py +3 -0
- app/api/__init__.py +0 -0
- app/api/deps.py +41 -0
- app/api/routes/__init__.py +0 -0
- app/api/routes/nginx.py +66 -0
- app/api/routes/scripts.py +79 -0
- app/core/__init__.py +0 -0
- app/core/config.py +39 -0
- app/core/logging.py +41 -0
- app/core/security.py +13 -0
- app/main.py +112 -0
- app/models/__init__.py +0 -0
- app/models/schemas.py +195 -0
- app/services/__init__.py +0 -0
- app/services/devops.py +203 -0
- app/templates/angular_setup.j2 +10 -0
- app/templates/apache_setup.j2 +18 -0
- app/templates/auto_updates_setup.j2 +8 -0
- app/templates/backups_setup.j2 +5 -0
- app/templates/caddy_setup.j2 +20 -0
- app/templates/cherokee_setup.j2 +8 -0
- app/templates/disable_root_setup.j2 +10 -0
- app/templates/docker_compose_setup.j2 +9 -0
- app/templates/docker_setup.j2 +9 -0
- app/templates/dotnet_setup.j2 +5 -0
- app/templates/fail2ban_setup.j2 +8 -0
- app/templates/go_setup.j2 +5 -0
- app/templates/haproxy_setup.j2 +38 -0
- app/templates/java_setup.j2 +5 -0
- app/templates/litespeed_setup.j2 +13 -0
- app/templates/mariadb_setup.j2 +16 -0
- app/templates/mongodb_setup.j2 +19 -0
- app/templates/monitoring_setup.j2 +7 -0
- app/templates/mysql_setup.j2 +16 -0
- app/templates/nginx/nginx_docker.conf.j2 +13 -0
- app/templates/nginx/nginx_dotnet.conf.j2 +13 -0
- app/templates/nginx/nginx_go.conf.j2 +13 -0
- app/templates/nginx/nginx_java.conf.j2 +13 -0
- app/templates/nginx/nginx_nodejs.conf.j2 +13 -0
- app/templates/nginx/nginx_php.conf.j2 +18 -0
- app/templates/nginx/nginx_python.conf.j2 +13 -0
- 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 |
+
}
|