feat: CI/CD, per-domain HTTPS, backup, config generator

- sites.yml — единый источник истины для всех сайтов
- generate-configs.sh — генератор nginx конфигов, certbot domains.txt, .env
- nginx: per-domain HTTPS (вместо all-or-nothing switch-config)
- certbot: единый renew-all.sh, динамический init (без 5 дублирующих скриптов)
- backup: контейнер с pg_dump + rclone (Яндекс.Диск), ежедневно в 3AM
- Gitea + Gitea Runner в docker-compose (self-hosted Git + CI/CD)
- .gitea/workflows/deploy.yml — CI/CD pipeline: push → авто-деплой
- Makefile: generate-configs, reconfig, deploy, backup, restore, gitea, help
This commit is contained in:
valitovgaziz
2026-06-12 12:22:19 +05:00
parent abcb327278
commit 8e766b540e
31 changed files with 1535 additions and 343 deletions
+52
View File
@@ -0,0 +1,52 @@
name: Deploy
on:
push:
branches: [main]
paths:
- 'main_dc/**'
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Deploy
run: |
cd /home/gaziz/artefacts/tp/main_dc
git pull origin main
# Если изменился sites.yml — генерируем конфиги
if git diff --name-only HEAD~1 HEAD | grep -q 'main_dc/sites.yml'; then
echo "→ sites.yml changed, generating configs..."
bash generate-configs.sh
fi
# Авто-детект и пересборка изменённых сервисов
echo "→ Detecting changed services..."
CHANGED=$(git diff --name-only HEAD~1 HEAD | grep -oP 'main_dc/\K[^/]+' | sort -u)
for svc in $CHANGED; do
svc_name="$svc"
# маппинг директорий на имена compose-сервисов
case "$svc" in
BB) svc_name="api_bb" ;;
valitovgaziz) svc_name="valitovgaziz" ;;
nginx|certbot|backup|gitea) svc_name="$svc" ;;
api_bb|api_yal|analytics|db) svc_name="$svc" ;;
yalarba) svc_name="yalarba" ;;
*) svc_name="" ;;
esac
if [ -n "$svc_name" ] && grep -q "^ $svc_name:" docker-compose.yml; then
echo " → Rebuilding $svc_name..."
make stop_$svc_name build_$svc_name start_$svc_name || \
make stop_$svc build_$svc start_$svc 2>/dev/null || \
true
fi
done
# Nginx всегда перезапускаем если изменились конфиги
if echo "$CHANGED" | grep -q 'nginx\|sites.yml'; then
echo " → Reloading nginx..."
docker compose exec -T nginx nginx -s reload 2>/dev/null || \
docker compose restart nginx
fi
+6 -7
View File
@@ -1,12 +1,11 @@
#CERTBOT NGINX VARIABLES
EMAIL=valitovgaziz@yandex.ru
DOMAINS_yalarba=yalarba.ru,www.yalarba.ru
DOMAINS_valitovgaziz=valitovgaziz.ru,www.valitovgaziz.ru
DOMAINS_easysite102=easysite102.ru,www.easysite102.ru
DOMAINS_begushiybashkir=xn--80abahjtcfl5d0a8di.xn--p1ai,www.xn--80abahjtcfl5d0a8di.xn--p1ai
DOMAINS_begushiybashkir_latin=begushiybashkir.ru,www.begushiybashkir.ru
#CERTBOT NGINX VARIABLES — авто-сгенерировано, не редактировать вручную
ALL_DOMAINS=yalarba.ru,www.yalarba.ru,valitovgaziz.ru,www.valitovgaziz.ru,easysite102.ru,www.easysite102.ru,begushiybashkir.ru,www.begushiybashkir.ru,xn--80abahjtcfl5d0a8di.xn--p1ai,www.xn--80abahjtcfl5d0a8di.xn--p1ai
DOMAINS_begushiybashkir=begushiybashkir.ru,www.begushiybashkir.ru
DOMAINS_begushiybashkir_idn=xn--80abahjtcfl5d0a8di.xn--p1ai,www.xn--80abahjtcfl5d0a8di.xn--p1ai
DOMAINS_easysite102=easysite102.ru,www.easysite102.ru
DOMAINS_valitovgaziz=valitovgaziz.ru,www.valitovgaziz.ru
DOMAINS_yalarba=yalarba.ru,www.yalarba.ru
# keycloak
KEYCLOAK_ADMIN_PASSWORD=your_secure_password
+117
View File
@@ -238,3 +238,120 @@ start_yalarba:
# Полный цикл обновления yalarba-nuxt
yalarba: stop_yalarba git build_yalarba start_yalarba wn
# ═══════════════════════════════════════════════
# НОВЫЕ ЦЕЛИ: generate-configs, deploy, backup
# ═══════════════════════════════════════════════
# Генерация конфигов из sites.yml
generate-configs:
bash generate-configs.sh
# Генерация + рестарт nginx
reconfig: generate-configs
docker compose restart nginx
$(MAKE) wn
# Авто-детект изменённых сервисов и деплой только их
deploy: git
@echo "=== Detecting changes ==="
@CHANGED=$$(git diff --name-only HEAD~1 HEAD | grep -oP 'main_dc/\K[^/]+' | sort -u); \
for svc in $$CHANGED; do \
case "$$svc" in \
BB) name="api_bb" ;; \
certbot) name="certbot" ;; \
backup) name="backup" ;; \
gitea) name="gitea" ;; \
*) name="$$svc" ;; \
esac; \
if grep -q "^ $$name:" docker-compose.yml 2>/dev/null; then \
echo " → Rebuilding $$name..."; \
$(MAKE) stop_$$name build_$$name start_$$name 2>/dev/null || \
$(MAKE) stop_$$svc build_$$svc start_$$svc 2>/dev/null || true; \
fi; \
done; \
if echo "$$CHANGED" | grep -q 'sites.yml\|nginx'; then \
echo " → Regenerating configs..."; \
bash generate-configs.sh; \
docker compose restart nginx; \
fi
# Ручной запуск бэкапа
backup:
docker compose exec backup /opt/backup.sh
# Ручной запуск бэкапа (разовый контейнер)
backup-run:
docker compose run --rm backup /opt/backup.sh
# Восстановление из бэкапа: make restore [DATE=2026-06-11]
restore:
docker compose run --rm backup /opt/restore.sh $(DATE)
# Gitea — полный цикл обновления
gitea: stop_gitea git build_gitea start_gitea wn
stop_gitea:
docker compose down gitea
build_gitea:
docker compose build gitea --no-cache
start_gitea:
docker compose up gitea -d
# Gitea Runner — полный цикл
gitea-runner: stop_gitea-runner git build_gitea-runner start_gitea-runner wn
stop_gitea-runner:
docker compose down gitea-runner
build_gitea-runner:
docker compose build gitea-runner --no-cache
start_gitea-runner:
docker compose up gitea-runner -d
# Gitea first-time setup helper
gitea-setup:
@echo "=== Gitea Setup ==="
@echo "1. Open http://94.41.23.97:3001 in browser"
@echo "2. Complete initial setup (DB: SQLite3 is fine)"
@echo "3. Create admin user"
@echo "4. Create new repository 'tp' and push:"
@echo " git remote add gitea http://94.41.23.97:3001/USER/tp.git"
@echo " git push -u gitea main"
@echo "5. Register runner:"
@echo " Settings → Actions → Runners → Create Token"
@echo " Update GITEA_RUNNER_REGISTRATION_TOKEN in docker-compose.yml"
@echo " Then: docker compose up -d gitea-runner"
@echo "6. Add secrets in repo Settings → Actions → Secrets:"
@echo " (none needed — runner runs locally)"
# Показать все доступные цели
help:
@echo "=== Make targets ==="
@echo ""
@echo "Site management:"
@echo " generate-configs — generate nginx configs from sites.yml"
@echo " reconfig — generate configs + restart nginx"
@echo ""
@echo "Deploy:"
@echo " all — full cycle all services"
@echo " deploy — auto-detect changes, rebuild only changed"
@echo " <service> — full cycle for one service"
@echo ""
@echo "Backup:"
@echo " backup — run backup via running container"
@echo " backup-run — run backup in one-shot container"
@echo " restore DATE=... — restore from backup"
@echo ""
@echo "Gitea:"
@echo " gitea — full cycle Gitea"
@echo " gitea-runner — full cycle Runner"
@echo " gitea-setup — first-time setup instructions"
@echo ""
@echo "Monitoring:"
@echo " wn — watch docker ps"
@echo " logs_<service> — logs for a service"
@echo " bb_db — psql into bb_db"
+11
View File
@@ -0,0 +1,11 @@
FROM alpine:3.19
RUN apk add --no-cache postgresql-client rclone bash curl
COPY scripts/ /opt/
RUN chmod +x /opt/*.sh
# crontab для расписания бэкапов
RUN echo "$BACKUP_TIME /opt/backup.sh > /proc/1/fd/1 2>&1" > /etc/crontabs/root
CMD ["crond", "-f", "-l", "2"]
+8
View File
@@ -0,0 +1,8 @@
# Пример конфига rclone для Яндекс.Диска
# Скопируй в backup/rclone.conf и заполни токен
# Инструкция: https://rclone.org/yandex/
[yadisk]
type = yandex
client_id =
client_secret =
token = {"access_token":"...","token_type":"...","expiry":"..."}
+47
View File
@@ -0,0 +1,47 @@
#!/bin/bash
# backup.sh — ежедневный бэкап: pg_dump + файлы → локально + Яндекс.Диск
set -euo pipefail
BACKUP_DIR="/backups/$(date +%Y-%m-%d)"
RETENTION_DAYS="${BACKUP_RETENTION_DAYS:-7}"
DB_NAMES="${DB_NAMES:-mydb}"
TIMESTAMP=$(date +%H%M%S)
mkdir -p "$BACKUP_DIR/db" "$BACKUP_DIR/files"
echo "=== Backup $TIMESTAMP ==="
# 1. Дампы всех БД
IFS=',' read -ra databases <<< "$DB_NAMES"
for db in "${databases[@]}"; do
db=$(echo "$db" | xargs) # trim
echo "→ Dumping database: $db"
PGPASSWORD="$DB_PASSWORD" pg_dump -h "$DB_HOST" -p "${DB_PORT:-5432}" \
-U "$DB_USER" -d "$db" --format=custom \
-f "$BACKUP_DIR/db/${db}-${TIMESTAMP}.dump"
done
# 2. Архив файлов
echo "→ Archiving files..."
tar -czf "$BACKUP_DIR/files/certbot-${TIMESTAMP}.tar.gz" -C /data/certbot . 2>/dev/null || true
tar -czf "$BACKUP_DIR/files/uploads-${TIMESTAMP}.tar.gz" -C /data/uploads . 2>/dev/null || true
tar -czf "$BACKUP_DIR/files/analytics-${TIMESTAMP}.tar.gz" -C /data/analytics . 2>/dev/null || true
# 3. Создаём symlink latest
rm -f /backups/latest
ln -sf "$BACKUP_DIR" /backups/latest
# 4. Ротация — удаляем старше RETENTION_DAYS
find /backups -maxdepth 1 -type d -name '2*' -mtime "+$RETENTION_DAYS" -exec rm -rf {} \; 2>/dev/null || true
echo "✓ Local backup saved to $BACKUP_DIR"
# 5. Синхронизация с Яндекс.Диск
if command -v rclone > /dev/null 2>&1 && [ -n "${RCLONE_REMOTE:-}" ]; then
echo "→ Syncing to cloud: $RCLONE_REMOTE"
rclone sync /backups "$RCLONE_REMOTE" --progress 2>&1 || \
echo " ⚠ Cloud sync failed (check rclone config)"
echo "✓ Cloud sync complete"
fi
echo "=== Backup finished ==="
+43
View File
@@ -0,0 +1,43 @@
#!/bin/bash
# restore.sh — восстановление из бэкапа
# Использование: docker compose run --rm backup /opt/restore.sh [дата]
set -euo pipefail
BACKUP_DATE="${1:-latest}"
BACKUP_DIR="/backups/$BACKUP_DATE"
if [ ! -d "$BACKUP_DIR" ]; then
echo "Ошибка: бэкап $BACKUP_DIR не найден"
echo "Доступные бэкапы:"
ls -d /backups/2* 2>/dev/null || echo " (нет бэкапов)"
exit 1
fi
echo "=== Restore from $BACKUP_DIR ==="
# Восстановить БД
if [ -d "$BACKUP_DIR/db" ]; then
for dump in "$BACKUP_DIR/db"/*.dump; do
[ -f "$dump" ] || continue
db=$(basename "$dump" | sed 's/-.*//')
echo "→ Restoring database: $db"
PGPASSWORD="$DB_PASSWORD" pg_restore -h "$DB_HOST" -p "${DB_PORT:-5432}" \
-U "$DB_USER" -d "$db" --clean --if-exists "$dump" || \
echo " ⚠ Restore of $db had warnings (non-fatal)"
done
fi
# Распаковать файлы
if [ -d "$BACKUP_DIR/files" ]; then
for archive in "$BACKUP_DIR/files"/*.tar.gz; do
[ -f "$archive" ] || continue
name=$(basename "$archive" | sed 's/-.*//')
target="/data/$name"
echo "→ Extracting $name to $target"
mkdir -p "$target"
tar -xzf "$archive" -C "$target" || true
done
fi
echo "=== Restore completed ==="
echo "При необходимости перезапусти сервисы: docker compose restart"
+2 -12
View File
@@ -1,20 +1,10 @@
FROM certbot/certbot
# Проверяем наличие crond (используем command -v вместо which)
RUN if ! command -v crond > /dev/null 2>&1; then \
echo "Cron not found. Installing cronie..."; \
apk add --no-cache cronie; \
else \
echo "Cron is already installed."; \
fi
RUN apk add --no-cache cronie docker-cli
# Создаем директории для конфигов
RUN mkdir -p /etc/letsencrypt/config
# Копируем конфигурационные файлы
COPY scripts/ /opt/
# Устанавливаем права
RUN chmod +x /opt/*
RUN chmod +x /opt/*.sh
ENTRYPOINT ["/opt/init-certbot.sh"]
+10
View File
@@ -0,0 +1,10 @@
yalarba.ru
www.yalarba.ru
valitovgaziz.ru
www.valitovgaziz.ru
easysite102.ru
www.easysite102.ru
begushiybashkir.ru
www.begushiybashkir.ru
xn--80abahjtcfl5d0a8di.xn--p1ai
www.xn--80abahjtcfl5d0a8di.xn--p1ai
+1 -1
View File
@@ -1 +1 @@
0 0 * * * root /opt/checkRenewCerts.sh > /proc/1/fd/1 2>&1
0 0 * * * /opt/renew-all.sh > /proc/1/fd/1 2>&1
+23 -60
View File
@@ -1,69 +1,32 @@
#!/bin/sh
# init-certbot.sh — точка входа certbot контейнера
set -e
# Проверяем наличие сертификатов для yalarba.ru
if [ ! -d "/etc/letsencrypt/live/yalarba.ru" ]; then
echo "Получаем новые сертификаты yalarba.ru ..."
echo "=== Certbot init ==="
# Получаем сертификаты для всех доменов из DOMAINS_* env
env | grep '^DOMAINS_' | grep -v '^ALL_DOMAINS' | sort | while IFS='=' read -r var_name domains; do
primary_domain=$(echo "$domains" | cut -d, -f1)
if [ ! -d "/etc/letsencrypt/live/$primary_domain" ]; then
echo "→ Получаем сертификат для $primary_domain"
certbot certonly --webroot \
--config /etc/letsencrypt/config/certbot.ini \
-w /var/www/certbot \
-d ${DOMAINS_yalarba}
fi
-d "$domains"
echo "✓ Сертификат для $primary_domain получен"
else
echo "✓ Сертификат для $primary_domain уже существует"
fi
done
echo "сertificates for ${DOMAINS_yalarba} is ready"
# Проверяем наличие сертификатов для valitovgaziz.ru
if [ ! -d "/etc/letsencrypt/live/valitovgaziz.ru" ]; then
echo "Получаем новые сертификаты valitovgaziz ..."
certbot certonly --webroot \
--config /etc/letsencrypt/config/certbot.ini \
-w /var/www/certbot \
-d ${DOMAINS_valitovgaziz}
fi
echo "сertificates for ${DOMAINS_valitovgaziz} is ready"
# Проверяем наличие сертификатов для easysite102.ru
if [ ! -d "/etc/letsencrypt/live/easysite102.ru" ]; then
echo "Получаем новые сертификаты easysite102.ru ..."
certbot certonly --webroot \
--config /etc/letsencrypt/config/certbot.ini \
-w /var/www/certbot \
-d ${DOMAINS_easysite102}
fi
echo "сertificates for ${DOMAINS_easysite102} is ready"
# Проверяем наличие сертификатов для бегущийбашкир.рф
if [ ! -d "/etc/letsencrypt/live/xn--80abahjtcfl5d0a8di.xn--p1ai" ]; then
echo "Получаем новые сертификаты xn--80abahjtcfl5d0a8di.xn--p1ai(бегущийбашкир.рф) ..."
certbot certonly --webroot \
--config /etc/letsencrypt/config/certbot.ini \
-w /var/www/certbot \
-d ${DOMAINS_begushiybashkir}
fi
echo "сertificates for ${DOMAINS_begushiybashkir} is ready"
# Проверяем наличие сертификатов для begushiybashkir.ru
if [ ! -d "/etc/letsencrypt/live/begushiybashkir.ru" ]; then
echo "Получаем новые сертификаты begushiybashkir.ru ..."
certbot certonly --webroot \
--config /etc/letsencrypt/config/certbot.ini \
-w /var/www/certbot \
-d ${DOMAINS_begushiybashkir_latin}
fi
echo "сertificates for ${DOMAINS_begushiybashkir_latin} is ready"
set -e # Завершаем работу, если любая команда вернёт ошибку
# Активируем сервис cron
/usr/sbin/crond -f &
crond -f &
# Копируем нашу собственную crontab таблицу
# Настраиваем cron для ежедневного обновления
cp /opt/crontab.txt /etc/crontabs/root
# Оставляем контейнер открытым
tail -f /dev/null
# Запускаем crond в фоне
crond -b
echo "=== Init завершён, контейнер работает ==="
# Держим контейнер живым
tail -f /dev/null
+17
View File
@@ -0,0 +1,17 @@
#!/bin/sh
# renew-all.sh — единый скрипт обновления всех сертификатов
set -e
echo "=== Certbot renewal ==="
# Обновляем все сертификаты
certbot renew --webroot -w /var/www/certbot
# Перезагружаем nginx чтобы он подхватил новые сертификаты
if command -v docker > /dev/null 2>&1; then
echo "→ Перезагружаем nginx..."
docker exec nginx nginx -s reload 2>/dev/null || \
echo " (nginx reload не удался, возможно контейнер не запущен)"
fi
echo "=== Renewal завершён ==="
+80 -8
View File
@@ -10,19 +10,15 @@ services:
- ./certbot/config:/etc/letsencrypt/config
- certbot_data:/etc/letsencrypt
- certbot_www:/var/www/certbot
- /var/run/docker.sock:/var/run/docker.sock
env_file:
- .env
environment:
- EMAIL=${EMAIL}
- DOMAINS=${ALL_DOMAINS}
- STAGING=0
restart: unless-stopped
healthcheck:
test:
[
"CMD-SHELL",
"test -f /etc/letsencrypt/live/$$(echo $${DOMAINS} | cut -d',' -f1)/fullchain.pem || exit 1",
]
test: ["CMD-SHELL", "ls /etc/letsencrypt/live/*/fullchain.pem 2>/dev/null | head -1 | xargs test -f || exit 1"]
interval: 30s
timeout: 10s
retries: 3
@@ -45,6 +41,7 @@ services:
- ./stubSite:/usr/share/nginx/stub/html
- ./BB/bbvue/dist:/usr/share/nginx/begushiybashkir/html
- analytics_logs:/var/log/analytics:ro
- ./nginx/conf.available:/etc/nginx/conf.available:ro
networks:
- web-network
- internal
@@ -52,8 +49,6 @@ services:
depends_on:
easysite:
condition: service_healthy
certbot:
condition: service_healthy
api_bb:
condition: service_healthy
analytics:
@@ -254,6 +249,81 @@ services:
timeout: 10s
retries: 3
# ──────────────────────────────────────────────
# Gitea — self-hosted Git сервер + CI/CD
# ──────────────────────────────────────────────
gitea:
image: gitea/gitea:latest
container_name: gitea
restart: unless-stopped
ports:
- "3001:3000"
- "2222:22"
volumes:
- gitea_data:/data
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
environment:
- USER_UID=1000
- USER_GID=1000
- GITEA__server__DOMAIN=git.yalarba.ru
- GITEA__server__SSH_DOMAIN=94.41.23.97
- GITEA__server__ROOT_URL=https://git.yalarba.ru
networks:
- web-network
- internal
healthcheck:
test: ["CMD", "wget", "--spider", "http://localhost:3000"]
interval: 30s
timeout: 10s
retries: 3
gitea-runner:
image: gitea/act_runner:latest
container_name: gitea-runner
restart: unless-stopped
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- /home/gaziz/artefacts/tp:/home/gaziz/artefacts/tp
- gitea_runner:/data
environment:
- GITEA_INSTANCE_URL=http://gitea:3000
- GITEA_RUNNER_REGISTRATION_TOKEN=
depends_on:
gitea:
condition: service_healthy
networks:
- internal
# ──────────────────────────────────────────────
# Backup — ежедневные бэкапы БД + файлов → локально + Яндекс.Диск
# ──────────────────────────────────────────────
backup:
build:
context: ./backup
dockerfile: Dockerfile
container_name: backup
restart: unless-stopped
volumes:
- /var/backups/tp:/backups
- certbot_data:/data/certbot:ro
- api_bb_uploads:/data/uploads:ro
- analytics_data:/data/analytics:ro
environment:
DB_HOST: db
DB_PORT: 5432
DB_USER: postgres
DB_PASSWORD: postgres
DB_NAMES: mydb,bb_db
RCLONE_REMOTE: "yadisk:tp-backups"
BACKUP_RETENTION_DAYS: 7
BACKUP_TIME: "0 3 * * *"
depends_on:
db:
condition: service_healthy
networks:
- internal
volumes:
certbot_data: # volume для данных Certbot
certbot_www: # volume для данных Certbot
@@ -261,6 +331,8 @@ volumes:
api_bb_uploads: # Volume для загружаемых файлов бегущий башкир
analytics_logs: # Volume для логов аналитики
analytics_data: # Volume для данных аналитики
gitea_data: # Volume для Gitea
gitea_runner: # Volume для Gitea Runner
networks:
web-network:
+474
View File
@@ -0,0 +1,474 @@
#!/bin/bash
# generate-configs.sh — генератор конфигов из sites.yml
# Генерирует: nginx-http.conf, nginx-ssl.conf, certbot/domains.txt, обновляет .env
set -euo pipefail
DIR="$(cd "$(dirname "$0")" && pwd)"
cd "$DIR"
SITES_YML="$DIR/sites.yml"
NGINX_DIR="$DIR/nginx"
ENV_FILE="$DIR/.env"
if [ ! -f "$SITES_YML" ]; then
echo "Ошибка: $SITES_YML не найден"
exit 1
fi
echo "=== Генерация конфигов из sites.yml ==="
# Используем python3 с quoted heredoc — предотвращает интерпретацию $ переменных bash
python3 - "$DIR" "$NGINX_DIR" "$ENV_FILE" << 'PYEOF'
import yaml, os, sys
BASE_DIR = sys.argv[1]
NGINX_DIR = sys.argv[2]
ENV_FILE = sys.argv[3]
SITES_YML = os.path.join(BASE_DIR, "sites.yml")
with open(SITES_YML) as f:
data = yaml.safe_load(f)
sites = data.get("sites", {})
if not sites:
print("Ошибка: в sites.yml нет сайтов")
sys.exit(1)
# собираем данные
all_domains = []
env_domains = {}
site_list = []
for name, cfg in sites.items():
domain = cfg["domain"]
aliases = cfg.get("aliases", [])
all_domains.append(domain)
all_domains.extend(aliases)
env_key = f"DOMAINS_{name}"
env_val = ",".join([domain] + aliases)
env_domains[env_key] = env_val
site_list.append({
"name": name,
"domain": domain,
"aliases": aliases,
"type": cfg.get("type", "upstream"),
"upstream": cfg.get("upstream", ""),
"root": cfg.get("root", ""),
"api": cfg.get("api", {}),
})
env_domains["ALL_DOMAINS"] = ",".join(all_domains)
def all_server_names():
"""Возвращает строку со всеми доменами и алиасами через пробел"""
parts = []
for s in site_list:
parts.append(s["domain"])
parts.extend(s["aliases"])
return " ".join(parts)
def all_server_names_multiline():
"""Возвращает строку с переносами для nginx server_name"""
lines = []
for s in site_list:
lines.append(s["domain"])
for a in s["aliases"]:
lines.append(a)
return " \\\n ".join(lines)
# ──────────────────────────────────────────────
# 2. Генерация nginx-http.conf
# ──────────────────────────────────────────────
http_conf = f"""# Автоматически сгенерировано generate-configs.sh — не редактировать вручную
# HTTP-only конфигурация (работает когда нет сертификатов)
server {{
listen 80;
server_name {all_server_names_multiline()};
location / {{
root /usr/share/nginx/stub/html;
index index.html;
}}
location /.well-known/acme-challenge/ {{
root /var/www/certbot;
}}
}}
# Блок для HTTPS → HTTP редиректа (порт 443)
server {{
listen 443 ssl;
server_name {all_server_names_multiline()};
ssl_certificate /etc/nginx/ssl/dummy.crt;
ssl_certificate_key /etc/nginx/ssl/dummy.key;
return 301 http://$host$request_uri;
}}
"""
http_conf_path = os.path.join(NGINX_DIR, "nginx-http.conf")
with open(http_conf_path, "w") as f:
f.write(http_conf.lstrip())
print(f" ✓ {http_conf_path}")
# ──────────────────────────────────────────────
# 3. Генерация nginx-ssl.conf
# ──────────────────────────────────────────────
ssl_server_blocks = []
for s in site_list:
server_names = " ".join([s["domain"]] + s["aliases"])
block = f"""
server {{
listen 443 ssl;
server_name {server_names};
ssl_certificate /etc/letsencrypt/live/{s["domain"]}/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/{s["domain"]}/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
ssl_ciphers "EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH";
"""
if s["type"] == "upstream":
block += f"""
location / {{
proxy_pass {s["upstream"]};
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Port $server_port;
proxy_connect_timeout 600;
proxy_send_timeout 600;
proxy_read_timeout 600;
}}
"""
elif s["type"] == "static":
block += f"""
location / {{
root {s["root"]};
index index.html;
try_files $uri $uri/ /index.html;
}}
"""
# API routes
for path, target in s["api"].items():
cors_block = ""
if "/api/" in path:
cors_block = """
if ($request_method = OPTIONS) {
add_header 'Access-Control-Allow-Origin' "$http_origin";
add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, PATCH, DELETE';
add_header 'Access-Control-Allow-Headers' 'DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization';
add_header 'Access-Control-Max-Age' 1728000;
add_header 'Content-Length' 0;
add_header 'Content-Type' 'text/plain charset=UTF-8';
return 204;
}
"""
block += f"""
location {path} {{
proxy_pass {target};
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Port $server_port;
proxy_connect_timeout 600;
proxy_send_timeout 600;
proxy_read_timeout 600;
{cors_block}
}}
"""
if s["type"] == "static":
block += f"""
location /uploads/ {{
alias /uploads/;
expires 1y;
add_header Cache-Control "public, immutable";
}}
"""
block += "}"
ssl_server_blocks.append(block)
ssl_conf = f"""# Автоматически сгенерировано generate-configs.sh — не редактировать вручную
# Полная HTTPS конфигурация
# --- HTTP → HTTPS редирект ---
server {{
listen 80;
server_name {all_server_names()};
location /.well-known/acme-challenge/ {{
root /var/www/certbot;
}}
location /uploads/ {{
alias /uploads/;
expires 1y;
add_header Cache-Control "public, immutable";
}}
location / {{
return 301 https://$host$request_uri;
}}
}}
# --- HTTPS серверные блоки ---
{''.join(ssl_server_blocks)}
"""
ssl_conf_path = os.path.join(NGINX_DIR, "nginx-ssl.conf")
with open(ssl_conf_path, "w") as f:
f.write(ssl_conf.lstrip())
print(f" ✓ {ssl_conf_path}")
# ──────────────────────────────────────────────
# 4. Генерация per-domain конфигов (conf.available/)
# ──────────────────────────────────────────────
CONF_AVAILABLE = os.path.join(NGINX_DIR, "conf.available")
os.makedirs(CONF_AVAILABLE, exist_ok=True)
# 00-http.conf — базовый HTTP catch-all (всегда активен)
base_http = f"""# Автоматически сгенерировано generate-configs.sh
server {{
listen 80 default_server;
server_name _;
location / {{
root /usr/share/nginx/stub/html;
index index.html;
}}
location /.well-known/acme-challenge/ {{
root /var/www/certbot;
}}
}}
"""
path = os.path.join(CONF_AVAILABLE, "00-http.conf")
with open(path, "w") as f:
f.write(base_http.lstrip())
print(f" ✓ conf.available/00-http.conf")
# per-domain: SSL + HTTP fallback
ORDER = ["10", "20", "30", "40", "50", "60", "70", "80", "90"]
for idx, s in enumerate(site_list):
prefix = ORDER[idx] if idx < len(ORDER) else f"{90 + idx}"
safe_name = s["name"]
server_names = " ".join([s["domain"]] + s["aliases"])
# --- SSL variant ---
ssl_block = f"""# CERT_DOMAIN={s["domain"]}
# Автоматически сгенерировано generate-configs.sh
server {{
listen 443 ssl;
server_name {server_names};
ssl_certificate /etc/letsencrypt/live/{s["domain"]}/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/{s["domain"]}/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
ssl_ciphers "EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH";
"""
if s["type"] == "upstream":
ssl_block += f"""
location / {{
proxy_pass {s["upstream"]};
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Port $server_port;
proxy_connect_timeout 600;
proxy_send_timeout 600;
proxy_read_timeout 600;
}}
"""
elif s["type"] == "static":
ssl_block += f"""
location / {{
root {s["root"]};
index index.html;
try_files $uri $uri/ /index.html;
}}
"""
for path, target in s["api"].items():
cors = ""
if "/api/" in path:
cors = """
if ($request_method = OPTIONS) {
add_header 'Access-Control-Allow-Origin' "$http_origin";
add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, PATCH, DELETE';
add_header 'Access-Control-Allow-Headers' 'DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization';
add_header 'Access-Control-Max-Age' 1728000;
add_header 'Content-Length' 0;
add_header 'Content-Type' 'text/plain charset=UTF-8';
return 204;
}
"""
ssl_block += f"""
location {path} {{
proxy_pass {target};
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Port $server_port;
proxy_connect_timeout 600;
proxy_send_timeout 600;
proxy_read_timeout 600;
{cors}
}}
"""
if s["type"] == "static":
ssl_block += f"""
location /uploads/ {{
alias /uploads/;
expires 1y;
add_header Cache-Control "public, immutable";
}}
"""
ssl_block += "}"
ssl_path = os.path.join(CONF_AVAILABLE, f"{prefix}-{safe_name}.ssl.conf")
with open(ssl_path, "w") as f:
f.write(ssl_block.lstrip())
# --- HTTP fallback variant ---
http_block = f"""# HTTP fallback for {s["domain"]} (no SSL cert)
server {{
listen 80;
server_name {server_names};
"""
if s["type"] == "upstream":
http_block += f"""
location / {{
proxy_pass {s["upstream"]};
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_connect_timeout 600;
proxy_send_timeout 600;
proxy_read_timeout 600;
}}
"""
elif s["type"] == "static":
http_block += f"""
location / {{
root {s["root"]};
index index.html;
try_files $uri $uri/ /index.html;
}}
"""
for path, target in s["api"].items():
cors = ""
if "/api/" in path:
cors = """
if ($request_method = OPTIONS) {
add_header 'Access-Control-Allow-Origin' "$http_origin";
add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, PATCH, DELETE';
add_header 'Access-Control-Allow-Headers' 'DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization';
add_header 'Access-Control-Max-Age' 1728000;
add_header 'Content-Length' 0;
add_header 'Content-Type' 'text/plain charset=UTF-8';
return 204;
}
"""
http_block += f"""
location {path} {{
proxy_pass {target};
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_connect_timeout 600;
proxy_send_timeout 600;
proxy_read_timeout 600;
{cors}
}}
"""
if s["type"] == "static":
http_block += f"""
location /uploads/ {{
alias /uploads/;
expires 1y;
add_header Cache-Control "public, immutable";
}}
"""
http_block += "}"
http_path = os.path.join(CONF_AVAILABLE, f"{prefix}-{safe_name}.http.conf")
with open(http_path, "w") as f:
f.write(http_block.lstrip())
print(f" ✓ conf.available/{prefix}-{safe_name}.ssl.conf + .http.conf")
# ──────────────────────────────────────────────
# 5. Генерация certbot/domains.txt
# ──────────────────────────────────────────────
domains_txt_path = os.path.join(BASE_DIR, "certbot", "domains.txt")
with open(domains_txt_path, "w") as f:
for d in all_domains:
f.write(d + "\n")
print(f" ✓ {domains_txt_path}")
# ──────────────────────────────────────────────
# 6. Обновление .env
# ──────────────────────────────────────────────
if os.path.exists(ENV_FILE):
with open(ENV_FILE) as f:
env_lines = f.readlines()
else:
env_lines = []
new_env = []
for line in env_lines:
stripped = line.strip()
if stripped.startswith("DOMAINS_") or stripped.startswith("ALL_DOMAINS") or "CERTBOT NGINX VARIABLES" in stripped:
continue
new_env.append(line)
# удаляем пустые строки в начале
while new_env and not new_env[0].strip():
new_env.pop(0)
domain_keys = {k: v for k, v in env_domains.items()}
insert_idx = None
for i, line in enumerate(new_env):
if line.strip().startswith("EMAIL="):
insert_idx = i + 1
break
env_header = "#CERTBOT NGINX VARIABLES — авто-сгенерировано, не редактировать вручную\n"
domain_lines = [f"{k}={v}\n" for k, v in sorted(domain_keys.items())]
if insert_idx is not None:
new_env.insert(insert_idx, env_header)
for dl in reversed(domain_lines):
new_env.insert(insert_idx + 1, dl)
else:
new_env = [env_header] + domain_lines + new_env
with open(ENV_FILE, "w") as f:
f.writelines(new_env)
print(f" ✓ {ENV_FILE} (обновлён)")
print()
print("=== Генерация завершена ===")
print(f"Сгенерировано {len(site_list)} сайтов:")
for s in site_list:
print(f" • {s['domain']} ({s['type']})")
print()
print("Не забудь перезапустить nginx: docker compose restart nginx")
PYEOF
+6 -17
View File
@@ -1,28 +1,17 @@
FROM nginx:alpine
# Установка зависимостей
RUN apk add --no-cache bash openssl
# Создание директории для сертификатов
RUN mkdir -p /etc/nginx/ssl
# Генерация самоподписанных сертификатов (действительны 365 дней)
RUN openssl req -x509 -nodes -days 365 \
# dummy сертификаты для nginx (нужны чтобы nginx стартовал с любым конфигом)
RUN mkdir -p /etc/nginx/ssl && \
openssl req -x509 -nodes -days 365 \
-newkey rsa:2048 \
-keyout /etc/nginx/ssl/dummy.key \
-out /etc/nginx/ssl/dummy.crt \
-subj "/C=US/ST=State/L=City/O=Organization/CN=localhost"
# Копируем обе конфигурации
COPY nginx-http.conf /etc/nginx/nginx-http.conf
COPY nginx-ssl.conf /etc/nginx/nginx-ssl.conf
RUN mkdir -p /var/www/certbot /etc/nginx/conf.d /etc/nginx/conf.available
# Создаем симлинк по умолчанию на HTTP конфиг
RUN ln -sf /etc/nginx/nginx-http.conf /etc/nginx/conf.d/default.conf
# Скрипт для проверки сертификатов и переключения конфига
COPY switch-config.sh /docker-entrypoint.d/switch-config.sh
# per-domain entrypoint для проверки сертификатов
COPY entrypoint.sh /docker-entrypoint.d/switch-config.sh
RUN chmod +x /docker-entrypoint.d/switch-config.sh
# Создаем необходимые директории
RUN mkdir -p /var/www/certbot
+14
View File
@@ -0,0 +1,14 @@
# Автоматически сгенерировано generate-configs.sh
server {
listen 80 default_server;
server_name _;
location / {
root /usr/share/nginx/stub/html;
index index.html;
}
location /.well-known/acme-challenge/ {
root /var/www/certbot;
}
}
@@ -0,0 +1,38 @@
# HTTP fallback for yalarba.ru (no SSL cert)
server {
listen 80;
server_name yalarba.ru www.yalarba.ru;
location / {
proxy_pass http://yalarba:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_connect_timeout 600;
proxy_send_timeout 600;
proxy_read_timeout 600;
}
location /api/v1/ {
proxy_pass http://api_yal:8787;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_connect_timeout 600;
proxy_send_timeout 600;
proxy_read_timeout 600;
if ($request_method = OPTIONS) {
add_header 'Access-Control-Allow-Origin' "$http_origin";
add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, PATCH, DELETE';
add_header 'Access-Control-Allow-Headers' 'DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization';
add_header 'Access-Control-Max-Age' 1728000;
add_header 'Content-Length' 0;
add_header 'Content-Type' 'text/plain charset=UTF-8';
return 204;
}
}
}
@@ -0,0 +1,48 @@
# CERT_DOMAIN=yalarba.ru
# Автоматически сгенерировано generate-configs.sh
server {
listen 443 ssl;
server_name yalarba.ru www.yalarba.ru;
ssl_certificate /etc/letsencrypt/live/yalarba.ru/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/yalarba.ru/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
ssl_ciphers "EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH";
location / {
proxy_pass http://yalarba:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Port $server_port;
proxy_connect_timeout 600;
proxy_send_timeout 600;
proxy_read_timeout 600;
}
location /api/v1/ {
proxy_pass http://api_yal:8787;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Port $server_port;
proxy_connect_timeout 600;
proxy_send_timeout 600;
proxy_read_timeout 600;
if ($request_method = OPTIONS) {
add_header 'Access-Control-Allow-Origin' "$http_origin";
add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, PATCH, DELETE';
add_header 'Access-Control-Allow-Headers' 'DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization';
add_header 'Access-Control-Max-Age' 1728000;
add_header 'Content-Length' 0;
add_header 'Content-Type' 'text/plain charset=UTF-8';
return 204;
}
}
}
@@ -0,0 +1,38 @@
# HTTP fallback for valitovgaziz.ru (no SSL cert)
server {
listen 80;
server_name valitovgaziz.ru www.valitovgaziz.ru;
location / {
proxy_pass http://valitovgaziz/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_connect_timeout 600;
proxy_send_timeout 600;
proxy_read_timeout 600;
}
location /api/ {
proxy_pass http://analytics:3000/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_connect_timeout 600;
proxy_send_timeout 600;
proxy_read_timeout 600;
if ($request_method = OPTIONS) {
add_header 'Access-Control-Allow-Origin' "$http_origin";
add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, PATCH, DELETE';
add_header 'Access-Control-Allow-Headers' 'DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization';
add_header 'Access-Control-Max-Age' 1728000;
add_header 'Content-Length' 0;
add_header 'Content-Type' 'text/plain charset=UTF-8';
return 204;
}
}
}
@@ -0,0 +1,48 @@
# CERT_DOMAIN=valitovgaziz.ru
# Автоматически сгенерировано generate-configs.sh
server {
listen 443 ssl;
server_name valitovgaziz.ru www.valitovgaziz.ru;
ssl_certificate /etc/letsencrypt/live/valitovgaziz.ru/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/valitovgaziz.ru/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
ssl_ciphers "EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH";
location / {
proxy_pass http://valitovgaziz/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Port $server_port;
proxy_connect_timeout 600;
proxy_send_timeout 600;
proxy_read_timeout 600;
}
location /api/ {
proxy_pass http://analytics:3000/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Port $server_port;
proxy_connect_timeout 600;
proxy_send_timeout 600;
proxy_read_timeout 600;
if ($request_method = OPTIONS) {
add_header 'Access-Control-Allow-Origin' "$http_origin";
add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, PATCH, DELETE';
add_header 'Access-Control-Allow-Headers' 'DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization';
add_header 'Access-Control-Max-Age' 1728000;
add_header 'Content-Length' 0;
add_header 'Content-Type' 'text/plain charset=UTF-8';
return 204;
}
}
}
@@ -0,0 +1,38 @@
# HTTP fallback for easysite102.ru (no SSL cert)
server {
listen 80;
server_name easysite102.ru www.easysite102.ru;
location / {
proxy_pass http://easysite:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_connect_timeout 600;
proxy_send_timeout 600;
proxy_read_timeout 600;
}
location /api/v1/ {
proxy_pass http://api_yal:8787;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_connect_timeout 600;
proxy_send_timeout 600;
proxy_read_timeout 600;
if ($request_method = OPTIONS) {
add_header 'Access-Control-Allow-Origin' "$http_origin";
add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, PATCH, DELETE';
add_header 'Access-Control-Allow-Headers' 'DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization';
add_header 'Access-Control-Max-Age' 1728000;
add_header 'Content-Length' 0;
add_header 'Content-Type' 'text/plain charset=UTF-8';
return 204;
}
}
}
@@ -0,0 +1,48 @@
# CERT_DOMAIN=easysite102.ru
# Автоматически сгенерировано generate-configs.sh
server {
listen 443 ssl;
server_name easysite102.ru www.easysite102.ru;
ssl_certificate /etc/letsencrypt/live/easysite102.ru/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/easysite102.ru/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
ssl_ciphers "EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH";
location / {
proxy_pass http://easysite:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Port $server_port;
proxy_connect_timeout 600;
proxy_send_timeout 600;
proxy_read_timeout 600;
}
location /api/v1/ {
proxy_pass http://api_yal:8787;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Port $server_port;
proxy_connect_timeout 600;
proxy_send_timeout 600;
proxy_read_timeout 600;
if ($request_method = OPTIONS) {
add_header 'Access-Control-Allow-Origin' "$http_origin";
add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, PATCH, DELETE';
add_header 'Access-Control-Allow-Headers' 'DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization';
add_header 'Access-Control-Max-Age' 1728000;
add_header 'Content-Length' 0;
add_header 'Content-Type' 'text/plain charset=UTF-8';
return 204;
}
}
}
@@ -0,0 +1,39 @@
# HTTP fallback for begushiybashkir.ru (no SSL cert)
server {
listen 80;
server_name begushiybashkir.ru www.begushiybashkir.ru;
location / {
root /usr/share/nginx/begushiybashkir/html;
index index.html;
try_files $uri $uri/ /index.html;
}
location /api/ {
proxy_pass http://api_bb:8080/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_connect_timeout 600;
proxy_send_timeout 600;
proxy_read_timeout 600;
if ($request_method = OPTIONS) {
add_header 'Access-Control-Allow-Origin' "$http_origin";
add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, PATCH, DELETE';
add_header 'Access-Control-Allow-Headers' 'DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization';
add_header 'Access-Control-Max-Age' 1728000;
add_header 'Content-Length' 0;
add_header 'Content-Type' 'text/plain charset=UTF-8';
return 204;
}
}
location /uploads/ {
alias /uploads/;
expires 1y;
add_header Cache-Control "public, immutable";
}
}
@@ -0,0 +1,48 @@
# CERT_DOMAIN=begushiybashkir.ru
# Автоматически сгенерировано generate-configs.sh
server {
listen 443 ssl;
server_name begushiybashkir.ru www.begushiybashkir.ru;
ssl_certificate /etc/letsencrypt/live/begushiybashkir.ru/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/begushiybashkir.ru/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
ssl_ciphers "EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH";
location / {
root /usr/share/nginx/begushiybashkir/html;
index index.html;
try_files $uri $uri/ /index.html;
}
location /api/ {
proxy_pass http://api_bb:8080/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Port $server_port;
proxy_connect_timeout 600;
proxy_send_timeout 600;
proxy_read_timeout 600;
if ($request_method = OPTIONS) {
add_header 'Access-Control-Allow-Origin' "$http_origin";
add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, PATCH, DELETE';
add_header 'Access-Control-Allow-Headers' 'DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization';
add_header 'Access-Control-Max-Age' 1728000;
add_header 'Content-Length' 0;
add_header 'Content-Type' 'text/plain charset=UTF-8';
return 204;
}
}
location /uploads/ {
alias /uploads/;
expires 1y;
add_header Cache-Control "public, immutable";
}
}
@@ -0,0 +1,39 @@
# HTTP fallback for xn--80abahjtcfl5d0a8di.xn--p1ai (no SSL cert)
server {
listen 80;
server_name xn--80abahjtcfl5d0a8di.xn--p1ai www.xn--80abahjtcfl5d0a8di.xn--p1ai;
location / {
root /usr/share/nginx/begushiybashkir/html;
index index.html;
try_files $uri $uri/ /index.html;
}
location /api/ {
proxy_pass http://api_bb:8080/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_connect_timeout 600;
proxy_send_timeout 600;
proxy_read_timeout 600;
if ($request_method = OPTIONS) {
add_header 'Access-Control-Allow-Origin' "$http_origin";
add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, PATCH, DELETE';
add_header 'Access-Control-Allow-Headers' 'DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization';
add_header 'Access-Control-Max-Age' 1728000;
add_header 'Content-Length' 0;
add_header 'Content-Type' 'text/plain charset=UTF-8';
return 204;
}
}
location /uploads/ {
alias /uploads/;
expires 1y;
add_header Cache-Control "public, immutable";
}
}
@@ -0,0 +1,48 @@
# CERT_DOMAIN=xn--80abahjtcfl5d0a8di.xn--p1ai
# Автоматически сгенерировано generate-configs.sh
server {
listen 443 ssl;
server_name xn--80abahjtcfl5d0a8di.xn--p1ai www.xn--80abahjtcfl5d0a8di.xn--p1ai;
ssl_certificate /etc/letsencrypt/live/xn--80abahjtcfl5d0a8di.xn--p1ai/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/xn--80abahjtcfl5d0a8di.xn--p1ai/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
ssl_ciphers "EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH";
location / {
root /usr/share/nginx/begushiybashkir/html;
index index.html;
try_files $uri $uri/ /index.html;
}
location /api/ {
proxy_pass http://api_bb:8080/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Port $server_port;
proxy_connect_timeout 600;
proxy_send_timeout 600;
proxy_read_timeout 600;
if ($request_method = OPTIONS) {
add_header 'Access-Control-Allow-Origin' "$http_origin";
add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, PATCH, DELETE';
add_header 'Access-Control-Allow-Headers' 'DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization';
add_header 'Access-Control-Max-Age' 1728000;
add_header 'Content-Length' 0;
add_header 'Content-Type' 'text/plain charset=UTF-8';
return 204;
}
}
location /uploads/ {
alias /uploads/;
expires 1y;
add_header Cache-Control "public, immutable";
}
}
View File
+37
View File
@@ -0,0 +1,37 @@
#!/bin/bash
# entrypoint.sh — per-domain HTTPS переключение
# Для каждого домена проверяет сертификат и активирует SSL или HTTP конфиг
set -euo pipefail
CONF_AVAILABLE="/etc/nginx/conf.available"
CONF_D="/etc/nginx/conf.d"
CERT_DIR="/etc/letsencrypt/live"
rm -f "$CONF_D"/*.conf
# базовый HTTP (ACME challenge, catch-all redirect)
if [ -f "$CONF_AVAILABLE/00-http.conf" ]; then
ln -sf "$CONF_AVAILABLE/00-http.conf" "$CONF_D/00-http.conf"
fi
# per-domain конфиги
shopt -s nullglob
for ssl_conf in "$CONF_AVAILABLE"/*.ssl.conf; do
base="$(basename "$ssl_conf" .ssl.conf)"
http_conf="$CONF_AVAILABLE/$base.http.conf"
# CERT_DOMAIN в первой строке: # CERT_DOMAIN=example.ru
cert_domain="$(head -1 "$ssl_conf" | grep -oP '(?<=# CERT_DOMAIN=).+')" || true
if [ -n "$cert_domain" ] && [ -f "$CERT_DIR/$cert_domain/fullchain.pem" ]; then
ln -sf "$ssl_conf" "$CONF_D/$base.ssl.conf"
echo "$base → HTTPS ($cert_domain)"
elif [ -f "$http_conf" ]; then
ln -sf "$http_conf" "$CONF_D/$base.http.conf"
echo "$base → HTTP (no cert for $cert_domain)"
fi
done
echo "---"
ls -la "$CONF_D/" | grep -v '^total'
nginx -t
+17 -8
View File
@@ -1,16 +1,18 @@
# Автоматически сгенерировано generate-configs.sh — не редактировать вручную
# HTTP-only конфигурация (работает когда нет сертификатов)
server {
listen 80;
server_name yalarba.ru \
www.yalarba.ru \
easysite102.ru \
www.easysite102.ru \
valitovgaziz.ru \
www.valitovgaziz.ru \
xn--80abahjtcfl5d0a8di.xn--p1ai \
www.xn--80abahjtcfl5d0a8di.xn--p1ai \
easysite102.ru \
www.easysite102.ru \
begushiybashkir.ru \
www.begushiybashkir.ru \
auth.yalarba.ru;
xn--80abahjtcfl5d0a8di.xn--p1ai \
www.xn--80abahjtcfl5d0a8di.xn--p1ai;
location / {
root /usr/share/nginx/stub/html;
@@ -25,12 +27,19 @@ server {
# Блок для HTTPS → HTTP редиректа (порт 443)
server {
listen 443 ssl;
server_name yalarba.ru www.yalarba.ru easysite102.ru www.easysite102.ru valitovgaziz.ru www.valitovgaziz.ru xn--80abahjtcfl5d0a8di.xn--p1ai www.xn--80abahjtcfl5d0a8di.xn--p1ai begushiybashkir.ru www.begushiybashkir.ru;
server_name yalarba.ru \
www.yalarba.ru \
valitovgaziz.ru \
www.valitovgaziz.ru \
easysite102.ru \
www.easysite102.ru \
begushiybashkir.ru \
www.begushiybashkir.ru \
xn--80abahjtcfl5d0a8di.xn--p1ai \
www.xn--80abahjtcfl5d0a8di.xn--p1ai;
# Указание пустых сертификатов (обязательно для запуска Nginx)
ssl_certificate /etc/nginx/ssl/dummy.crt;
ssl_certificate_key /etc/nginx/ssl/dummy.key;
# Редирект всех HTTPS-запросов на HTTP
return 301 http://$host$request_uri;
}
+80 -221
View File
@@ -1,130 +1,52 @@
# ================================================
# КОНФИГУРАЦИЯ NGINX С ПОДДЕРЖКОЙ SSL
# Основные задачи:
# 1. Перенаправление HTTP → HTTPS
# 2. Обслуживание статических файлов
# 3. Проксирование к backend сервисам
# 4. Поддержка нескольких доменов
# ================================================
# Автоматически сгенерировано generate-configs.sh — не редактировать вручную
# Полная HTTPS конфигурация
# ================================================
# БЛОК 1: HTTP СЕРВЕР (ПОРТ 80)
# ================================================
# --- HTTP → HTTPS редирект ---
server {
# Прослушивание порта 80 для всех входящих HTTP соединений
listen 80;
# Список доменов, которые обслуживает этот сервер
# Все запросы к этим доменам по HTTP будут обработаны здесь
server_name yalarba.ru www.yalarba.ru
valitovgaziz.ru www.valitovgaziz.ru
easysite102.ru www.easysite102.ru
begushiybashkir.ru
xn--80abahjtcfl5d0a8di.xn--p1ai; # Punycode для IDN домена
server_name yalarba.ru www.yalarba.ru valitovgaziz.ru www.valitovgaziz.ru easysite102.ru www.easysite102.ru begushiybashkir.ru www.begushiybashkir.ru xn--80abahjtcfl5d0a8di.xn--p1ai www.xn--80abahjtcfl5d0a8di.xn--p1ai;
# ============================================
# ЛОКАЦИЯ: Проверочные файлы для Certbot
# ============================================
# Этот блок КРИТИЧЕСКИ ВАЖЕН для получения SSL сертификатов
# Certbot (Let's Encrypt) размещает здесь временные файлы
# для подтверждения владения доменом
location /.well-known/acme-challenge/ {
# Директория, где Certbot хранит проверочные файлы
root /var/www/certbot;
# Дополнительные настройки не нужны - nginx просто отдает файлы
}
# ============================================
# ЛОКАЦИЯ: Основное перенаправление
# ============================================
# Все HTTP запросы перенаправляются на HTTPS
# Это обеспечивает безопасность и правильную SEO-практику
location / {
# 301 - постоянный редирект (лучше для SEO, кэшируется браузерами)
# https://$host$request_uri - сохраняет домен и полный путь запроса
return 301 https://$host$request_uri;
# Пример:
# HTTP: http://example.com/page?param=1
# ↓ перенаправление ↓
# HTTPS: https://example.com/page?param=1
}
# ============================================
# ЛОКАЦИЯ: Загруженные файлы
# ============================================
# Обслуживание статических файлов (загрузок) по HTTP
# Может быть полезно для прямых ссылок или кэширования
location /uploads/ {
# Псевдоним пути - запросы к /uploads/ обслуживаются из /uploads/ на диске
alias /uploads/;
# Кэширование в браузере на 1 год
expires 1y;
# Заголовки кэширования:
# "public" - может кэшироваться прокси-серверами
# "immutable" - файлы никогда не меняются, браузер не проверяет обновления
add_header Cache-Control "public, immutable";
}
# Если файл не найден - вернуть 404 ошибку
try_files $uri =404;
location / {
return 301 https://$host$request_uri;
}
}
# ================================================
# БЛОК 2: HTTPS СЕРВЕР ДЛЯ YALARBA.RU
# ================================================
server {
# Прослушивание порта 443 с SSL/TLS шифрованием
listen 443 ssl;
# --- HTTPS серверные блоки ---
# Домены для этого сервера
server {
listen 443 ssl;
server_name yalarba.ru www.yalarba.ru;
# ============================================
# НАСТРОЙКИ SSL СЕРТИФИКАТОВ
# ============================================
# Пути к SSL сертификатам, сгенерированным Certbot
ssl_certificate /etc/letsencrypt/live/yalarba.ru/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/yalarba.ru/privkey.pem;
# ============================================
# НАСТРОЙКИ БЕЗОПАСНОСТИ SSL
# ============================================
# Разрешенные протоколы - только современные безопасные версии
ssl_protocols TLSv1.2 TLSv1.3;
# Сервер выбирает шифры (не клиент)
ssl_prefer_server_ciphers on;
# Список безопасных шифров
ssl_ciphers "EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH";
# ============================================
# ЛОКАЦИЯ: Nuxt 4 SSR приложение
# ============================================
location / {
# Проксирование к Nuxt.js SSR серверу
proxy_pass http://yalarba:3000;
# Полный набор заголовков для корректной работы приложения
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Port $server_port;
# Длинные таймауты
proxy_connect_timeout 600;
proxy_send_timeout 600;
proxy_read_timeout 600;
}
# ============================================
# ЛОКАЦИЯ: REST API (api_yal)
# ============================================
location /api/v1/ {
proxy_pass http://api_yal:8787;
proxy_set_header Host $host;
@@ -135,28 +57,30 @@ server {
proxy_connect_timeout 600;
proxy_send_timeout 600;
proxy_read_timeout 600;
if ($request_method = OPTIONS) {
add_header 'Access-Control-Allow-Origin' "$http_origin";
add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, PATCH, DELETE';
add_header 'Access-Control-Allow-Headers' 'DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization';
add_header 'Access-Control-Max-Age' 1728000;
add_header 'Content-Length' 0;
add_header 'Content-Type' 'text/plain charset=UTF-8';
return 204;
}
}
}
# ================================================
# БЛОК 3: HTTPS СЕРВЕР ДЛЯ VALITOVGAZIZ.RU
# ================================================
server {
listen 443 ssl;
server_name valitovgaziz.ru www.valitovgaziz.ru;
# Свой SSL сертификат для этого домена
ssl_certificate /etc/letsencrypt/live/valitovgaziz.ru/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/valitovgaziz.ru/privkey.pem;
# Те же настройки безопасности SSL
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
ssl_ciphers "EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH";
# ============================================
# ЛОКАЦИЯ: Проксирование к Vue SPA контейнеру
# ============================================
location / {
proxy_pass http://valitovgaziz/;
proxy_set_header Host $host;
@@ -169,79 +93,59 @@ server {
proxy_read_timeout 600;
}
# ============================================
# ЛОКАЦИЯ: API для аналитики
# ============================================
location /api/ {
proxy_pass http://analytics:3000/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
add_header Access-Control-Allow-Origin "*" always;
add_header Access-Control-Allow-Methods "POST, GET, OPTIONS" always;
add_header Access-Control-Allow-Headers "Content-Type, Authorization" always;
add_header Access-Control-Allow-Credentials "true" always;
proxy_set_header X-Forwarded-Port $server_port;
proxy_connect_timeout 600;
proxy_send_timeout 600;
proxy_read_timeout 600;
if ($request_method = OPTIONS) {
add_header 'Access-Control-Allow-Origin' "$http_origin";
add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, PATCH, DELETE';
add_header 'Access-Control-Allow-Headers' 'DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization';
add_header 'Access-Control-Max-Age' 1728000;
add_header 'Content-Length' 0;
add_header 'Content-Type' 'text/plain charset=UTF-8';
return 204;
}
proxy_connect_timeout 30s;
proxy_send_timeout 30s;
proxy_read_timeout 30s;
}
}
# ================================================
# БЛОК 4: HTTPS СЕРВЕР ДЛЯ EASYSITE102.RU
# ================================================
server {
listen 443 ssl;
server_name easysite102.ru www.easysite102.ru;
# Свой SSL сертификат
ssl_certificate /etc/letsencrypt/live/easysite102.ru/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/easysite102.ru/privkey.pem;
# Безопасные настройки SSL
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
ssl_ciphers "EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH";
# ============================================
# ЛОКАЦИЯ: Проксирование к Nuxt.js приложению
# ============================================
location / {
# ВСЕ запросы проксируются к Nuxt.js серверу
proxy_pass http://easysite:3000;
# Полный набор заголовков для корректной работы приложения
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Port $server_port;
# Длинные таймауты для работы приложения
proxy_connect_timeout 600;
proxy_send_timeout 600;
proxy_read_timeout 600;
}
# ============================================
# ЛОКАЦИЯ: API Backend для Easysite (api_yal)
# ============================================
location /api/v1/ {
proxy_pass http://api_yal:8787;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Port $server_port;
proxy_connect_timeout 600;
proxy_send_timeout 600;
proxy_read_timeout 600;
@@ -255,111 +159,26 @@ server {
add_header 'Content-Type' 'text/plain charset=UTF-8';
return 204;
}
}
}
# ================================================
# БЛОК 5: HTTPS СЕРВЕР ДЛЯ IDN ДОМЕНА
# (Punycode для "бегущийбашкир.рф")
# ================================================
server {
listen 443 ssl;
# Punycode представление кириллического домена
server_name xn--80abahjtcfl5d0a8di.xn--p1ai
www.xn--80abahjtcfl5d0a8di.xn--p1ai;
# Отдельный сертификат для IDN домена
ssl_certificate /etc/letsencrypt/live/xn--80abahjtcfl5d0a8di.xn--p1ai/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/xn--80abahjtcfl5d0a8di.xn--p1ai/privkey.pem;
# Стандартные SSL настройки
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
ssl_ciphers "EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH";
# ============================================
# ЛОКАЦИЯ: SPA приложение (такое же как begushiybashkir.ru)
# ============================================
location / {
root /usr/share/nginx/begushiybashkir/html;
index index.html;
try_files $uri $uri/ /index.html;
}
# ============================================
# ЛОКАЦИЯ: API для "Бегущий Башкир"
# ============================================
location /api/ {
proxy_pass http://api_bb:8080/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Port $server_port;
proxy_connect_timeout 600;
proxy_send_timeout 600;
proxy_read_timeout 600;
# Те же CORS настройки что и у Easysite
if ($request_method = OPTIONS ) {
add_header 'Access-Control-Allow-Origin' "$http_origin";
add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, PATCH, DELETE';
add_header 'Access-Control-Allow-Headers' 'DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization';
add_header 'Access-Control-Max-Age' 1728000;
add_header 'Content-Length' 0;
add_header 'Content-Type' 'text/plain charset=UTF-8';
return 204;
}
}
# ============================================
# ЛОКАЦИЯ: Загруженные файлы (статическое обслуживание)
# ============================================
location /uploads/ {
# Обслуживание файлов загрузок напрямую из файловой системы
alias /uploads/;
# Долгое кэширование - файлы загрузок редко меняются
expires 1y;
add_header Cache-Control "public, immutable";
# try_files не нужен - nginx сам проверит существование файла
}
}
# ================================================
# БЛОК 6: HTTPS СЕРВЕР ДЛЯ BEGUSHIYBASHKIR.RU
# (ДУБЛИРУЕТ БЛОК 5 С ДРУГИМ ДОМЕНОМ)
# ================================================
server {
listen 443 ssl;
server_name begushiybashkir.ru www.begushiybashkir.ru;
# Свой SSL сертификат для этого домена
ssl_certificate /etc/letsencrypt/live/begushiybashkir.ru/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/begushiybashkir.ru/privkey.pem;
# Стандартные SSL настройки
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
ssl_ciphers "EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH";
# ВНИМАНИЕ: Весь контент ниже ДОСЛОВНО ДУБЛИРУЕТ
# предыдущий серверный блок для IDN домена
# ============================================
# ЛОКАЦИЯ: SPA приложение
# ============================================
location / {
root /usr/share/nginx/begushiybashkir/html;
index index.html;
try_files $uri $uri/ /index.html;
}
# ============================================
# ЛОКАЦИЯ: API для "Бегущий Башкир"
# ============================================
location /api/ {
proxy_pass http://api_bb:8080/;
proxy_set_header Host $host;
@@ -371,8 +190,7 @@ server {
proxy_send_timeout 600;
proxy_read_timeout 600;
# Копия CORS настроек
if ($request_method = OPTIONS ) {
if ($request_method = OPTIONS) {
add_header 'Access-Control-Allow-Origin' "$http_origin";
add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, PATCH, DELETE';
add_header 'Access-Control-Allow-Headers' 'DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization';
@@ -381,17 +199,58 @@ server {
add_header 'Content-Type' 'text/plain charset=UTF-8';
return 204;
}
}
location /uploads/ {
alias /uploads/;
expires 1y;
add_header Cache-Control "public, immutable";
}
}
server {
listen 443 ssl;
server_name xn--80abahjtcfl5d0a8di.xn--p1ai www.xn--80abahjtcfl5d0a8di.xn--p1ai;
ssl_certificate /etc/letsencrypt/live/xn--80abahjtcfl5d0a8di.xn--p1ai/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/xn--80abahjtcfl5d0a8di.xn--p1ai/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
ssl_ciphers "EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH";
location / {
root /usr/share/nginx/begushiybashkir/html;
index index.html;
try_files $uri $uri/ /index.html;
}
location /api/ {
proxy_pass http://api_bb:8080/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Port $server_port;
proxy_connect_timeout 600;
proxy_send_timeout 600;
proxy_read_timeout 600;
if ($request_method = OPTIONS) {
add_header 'Access-Control-Allow-Origin' "$http_origin";
add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, PATCH, DELETE';
add_header 'Access-Control-Allow-Headers' 'DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization';
add_header 'Access-Control-Max-Age' 1728000;
add_header 'Content-Length' 0;
add_header 'Content-Type' 'text/plain charset=UTF-8';
return 204;
}
}
# ============================================
# ЛОКАЦИЯ: Загруженные файлы
# ============================================
location /uploads/ {
alias /uploads/;
expires 1y;
add_header Cache-Control "public, immutable";
}
}
# ================================================
# КОНЕЦ КОНФИГУРАЦИИ
# ================================================
+49
View File
@@ -0,0 +1,49 @@
# Единый источник истины для всех сайтов проекта
# Добавление нового сайта = одна секция в этом файле
# После изменений запусти: bash generate-configs.sh
sites:
yalarba:
domain: yalarba.ru
aliases:
- www.yalarba.ru
type: upstream
upstream: http://yalarba:3000
api:
/api/v1/: http://api_yal:8787
valitovgaziz:
domain: valitovgaziz.ru
aliases:
- www.valitovgaziz.ru
type: upstream
upstream: http://valitovgaziz/
api:
/api/: http://analytics:3000/
easysite102:
domain: easysite102.ru
aliases:
- www.easysite102.ru
type: upstream
upstream: http://easysite:3000
api:
/api/v1/: http://api_yal:8787
begushiybashkir:
domain: begushiybashkir.ru
aliases:
- www.begushiybashkir.ru
type: static
root: /usr/share/nginx/begushiybashkir/html
api:
/api/: http://api_bb:8080/
begushiybashkir_idn:
domain: xn--80abahjtcfl5d0a8di.xn--p1ai
aliases:
- www.xn--80abahjtcfl5d0a8di.xn--p1ai
type: static
root: /usr/share/nginx/begushiybashkir/html
api:
/api/: http://api_bb:8080/