From 5cb843b351e98487b9a5e73c4c2dbdd55f7edb20 Mon Sep 17 00:00:00 2001 From: Aleksandr Fenin Date: Thu, 23 Oct 2025 15:46:07 +0300 Subject: [PATCH] config/deployment-environments: add dev and prod deployment configurations --- Makefile | 69 ++++++++++++++++++++ docker-compose.dev.yml | 68 ++++++++++++++++++++ docker-compose.yml | 28 ++++++++- docs/deployment-guide.md | 132 +++++++++++++++++++++++++++++++++++++++ render.dev.yaml | 70 +++++++++++++++++++++ render.yaml | 49 +++++++++++++++ 6 files changed, 415 insertions(+), 1 deletion(-) create mode 100644 docker-compose.dev.yml create mode 100644 docs/deployment-guide.md create mode 100644 render.dev.yaml diff --git a/Makefile b/Makefile index a14889e..cd2e940 100644 --- a/Makefile +++ b/Makefile @@ -18,6 +18,23 @@ run-dev: JWT_ISSUER=strive-api \ JWT_AUDIENCE=strive-app \ JWT_CLOCK_SKEW=2m \ + CORS_ALLOWED_ORIGINS=http://localhost:3000,http://localhost:3001,http://localhost:4200,http://127.0.0.1:3000,http://127.0.0.1:4200 \ + CORS_ALLOWED_METHODS=GET,POST,PUT,DELETE,OPTIONS \ + CORS_ALLOWED_HEADERS=Accept,Authorization,Content-Type,X-Request-ID \ + CORS_EXPOSED_HEADERS=X-Request-ID \ + CORS_ALLOW_CREDENTIALS=true \ + CORS_MAX_AGE=86400 \ + COOKIE_SECURE=false \ + COOKIE_SAMESITE=Lax \ + COOKIE_DOMAIN= \ + RATE_LIMIT_ENABLED=true \ + RATE_LIMIT_AUTH_PER_MINUTE=5 \ + RATE_LIMIT_GENERAL_PER_MINUTE=60 \ + RATE_LIMIT_BURST_SIZE=10 \ + EXERCISEDB_ENABLED=true \ + EXERCISEDB_BASE_URL=https://exercise.hellogym.io \ + EXERCISEDB_TIMEOUT=30s \ + EXERCISEDB_RETRY_COUNT=3 \ go run ./cmd/server db-up: @@ -95,6 +112,58 @@ docker-logs-all: @echo "Showing all logs..." docker compose logs -f +# Development deployment commands +dev-up: + @echo "Starting development environment..." + docker compose -f docker-compose.dev.yml up -d + +dev-up-build: + @echo "Building and starting development environment..." + docker compose -f docker-compose.dev.yml up --build -d + +dev-down: + @echo "Stopping development environment..." + docker compose -f docker-compose.dev.yml down + +dev-restart: + @echo "Restarting development environment..." + docker compose -f docker-compose.dev.yml down + docker compose -f docker-compose.dev.yml up --build -d + +dev-logs: + @echo "Showing development logs..." + docker compose -f docker-compose.dev.yml logs -f app + +dev-logs-all: + @echo "Showing all development logs..." + docker compose -f docker-compose.dev.yml logs -f + +# Production deployment commands +prod-up: + @echo "Starting production environment..." + docker compose up -d + +prod-up-build: + @echo "Building and starting production environment..." + docker compose up --build -d + +prod-down: + @echo "Stopping production environment..." + docker compose down + +prod-restart: + @echo "Restarting production environment..." + docker compose down + docker compose up --build -d + +prod-logs: + @echo "Showing production logs..." + docker compose logs -f app + +prod-logs-all: + @echo "Showing all production logs..." + docker compose logs -f + build: go build -o bin/server ./cmd/server diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml new file mode 100644 index 0000000..87ba143 --- /dev/null +++ b/docker-compose.dev.yml @@ -0,0 +1,68 @@ +version: '3.8' + +services: + app: + build: . + ports: + - "8080:8080" + environment: + - PORT=8080 + - LOG_LEVEL=INFO + - LOG_FORMAT=json + - DB_HOST=postgres + - DB_PORT=5432 + - DB_USER=postgres + - DB_PASSWORD=password + - DB_NAME=strive + - DB_SSL_MODE=disable + - JWT_SECRET=dev-secret-key-12345-very-long-for-security + - JWT_ISSUER=strive-api + - JWT_AUDIENCE=strive-app + - JWT_CLOCK_SKEW=2m + - CORS_ALLOWED_ORIGINS=http://localhost:3000,http://localhost:3001,http://localhost:4200,https://your-dev-domain.com + - CORS_ALLOWED_METHODS=GET,POST,PUT,DELETE,OPTIONS + - CORS_ALLOWED_HEADERS=Accept,Authorization,Content-Type,X-Request-ID + - CORS_EXPOSED_HEADERS=X-Request-ID + - CORS_ALLOW_CREDENTIALS=true + - CORS_MAX_AGE=86400 + - COOKIE_SECURE=false + - COOKIE_SAMESITE=Lax + - COOKIE_DOMAIN= + - RATE_LIMIT_ENABLED=true + - RATE_LIMIT_AUTH_PER_MINUTE=5 + - RATE_LIMIT_GENERAL_PER_MINUTE=60 + - RATE_LIMIT_BURST_SIZE=10 + - EXERCISEDB_ENABLED=true + - EXERCISEDB_BASE_URL=https://exercise.hellogym.io + - EXERCISEDB_TIMEOUT=30s + - EXERCISEDB_RETRY_COUNT=3 + depends_on: + postgres: + condition: service_healthy + healthcheck: + test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:8080/health"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 40s + + postgres: + image: postgres:15-alpine + container_name: strive-postgres-dev + environment: + POSTGRES_DB: strive + POSTGRES_USER: postgres + POSTGRES_PASSWORD: password + ports: + - "5432:5432" + volumes: + - postgres_data_dev:/var/lib/postgresql/data + - ./migrations:/docker-entrypoint-initdb.d + healthcheck: + test: ["CMD-SHELL", "pg_isready -U postgres -d strive"] + interval: 5s + timeout: 5s + retries: 5 + +volumes: + postgres_data_dev: diff --git a/docker-compose.yml b/docker-compose.yml index 8d9281f..a22b7a0 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -15,7 +15,33 @@ services: - DB_PASSWORD=password - DB_NAME=strive - DB_SSL_MODE=disable - - JWT_SECRET=dev-secret-key-12345 + - JWT_SECRET=production-secret-key-very-long-and-secure + - JWT_ISSUER=strive-api + - JWT_AUDIENCE=strive-app + - JWT_CLOCK_SKEW=2m + - CORS_ALLOWED_ORIGINS=https://your-production-frontend.com + - CORS_ALLOWED_METHODS=GET,POST,PUT,DELETE,OPTIONS + - CORS_ALLOWED_HEADERS=Accept,Authorization,Content-Type,X-Request-ID + - CORS_EXPOSED_HEADERS=X-Request-ID + - CORS_ALLOW_CREDENTIALS=true + - CORS_MAX_AGE=86400 + - COOKIE_SECURE=true + - COOKIE_SAMESITE=Strict + - COOKIE_DOMAIN= + - RATE_LIMIT_ENABLED=true + - RATE_LIMIT_AUTH_PER_MINUTE=5 + - RATE_LIMIT_GENERAL_PER_MINUTE=60 + - RATE_LIMIT_BURST_SIZE=10 + - EXERCISEDB_ENABLED=true + - EXERCISEDB_BASE_URL=https://exercise.hellogym.io + - EXERCISEDB_TIMEOUT=30s + - EXERCISEDB_RETRY_COUNT=3 + - SECURITY_HSTS_MAX_AGE=31536000 + - SECURITY_HSTS_INCLUDE_SUBDOMAINS=true + - SECURITY_X_FRAME_OPTIONS=DENY + - SECURITY_X_CONTENT_TYPE_OPTIONS=nosniff + - SECURITY_REFERRER_POLICY=strict-origin-when-cross-origin + - SECURITY_XSS_PROTECTION=1; mode=block depends_on: postgres: condition: service_healthy diff --git a/docs/deployment-guide.md b/docs/deployment-guide.md new file mode 100644 index 0000000..2f47c0d --- /dev/null +++ b/docs/deployment-guide.md @@ -0,0 +1,132 @@ +# Deployment Guide + +## Локальная разработка + +### 1. Запуск локального окружения + +```bash +# Запуск базы данных +make db-up + +# Запуск приложения в dev режиме +make run-dev +``` + +### 2. Запуск через Docker (dev окружение) + +```bash +# Запуск dev окружения +make dev-up + +# Пересборка и запуск +make dev-up-build + +# Просмотр логов +make dev-logs + +# Остановка +make dev-down +``` + +### 3. Запуск через Docker (production окружение) + +```bash +# Запуск production окружения +make prod-up + +# Пересборка и запуск +make prod-up-build + +# Просмотр логов +make prod-logs + +# Остановка +make prod-down +``` + +## Деплой на Render.com + +### 1. Настройка переменных окружения + +В Render Dashboard настройте следующие переменные: + +#### Обязательные переменные: +- `JWT_SECRET` - секретный ключ (минимум 32 символа) +- `DB_HOST` - хост базы данных +- `DB_USER` - пользователь БД +- `DB_PASSWORD` - пароль БД +- `DB_NAME` - имя БД + +#### CORS настройки: +- `CORS_ALLOWED_ORIGINS` - разрешенные домены (через запятую) +- `CORS_ALLOW_CREDENTIALS=true` + +#### Cookie настройки: +- `COOKIE_SECURE=true` (для HTTPS) +- `COOKIE_SAMESITE=Strict` (для production) +- `COOKIE_DOMAIN=` (пустое для текущего домена) + +### 2. Деплой через Render + +#### Development стенд: +1. Подключите репозиторий к Render +2. Используйте `render.dev.yaml` для dev стенда +3. Настройте переменные окружения +4. Деплой произойдет автоматически при push в main + +#### Production стенд: +1. Используйте `render.yaml` для production +2. Настройте все переменные окружения +3. Убедитесь, что `CORS_ALLOWED_ORIGINS` содержит ваш production домен +4. Деплой произойдет автоматически при push в main + +### 3. Проверка деплоя + +```bash +# Проверка здоровья +curl https://your-app.onrender.com/health + +# Проверка детального статуса +curl https://your-app.onrender.com/health/detailed + +# Проверка CORS +curl -H "Origin: https://your-frontend.com" \ + -H "Access-Control-Request-Method: POST" \ + -H "Access-Control-Request-Headers: Content-Type" \ + -X OPTIONS \ + https://your-app.onrender.com/api/v1/auth/login +``` + +## Настройка для разных окружений + +### Development (локальная разработка) +- `COOKIE_SECURE=false` +- `COOKIE_SAMESITE=Lax` +- `CORS_ALLOWED_ORIGINS=http://localhost:3000,http://localhost:4200` + +### Staging (dev стенд) +- `COOKIE_SECURE=true` +- `COOKIE_SAMESITE=None` +- `CORS_ALLOWED_ORIGINS=https://your-dev-frontend.com` + +### Production +- `COOKIE_SECURE=true` +- `COOKIE_SAMESITE=Strict` +- `CORS_ALLOWED_ORIGINS=https://your-production-frontend.com` + +## Troubleshooting + +### Проблемы с куки +1. Убедитесь, что `CORS_ALLOW_CREDENTIALS=true` +2. Проверьте настройки `COOKIE_SECURE` и `COOKIE_SAMESITE` +3. Для cross-domain куки нужен `SameSite=None` и `Secure=true` + +### Проблемы с CORS +1. Проверьте `CORS_ALLOWED_ORIGINS` +2. Убедитесь, что домен точно совпадает +3. Проверьте, что `CORS_ALLOW_CREDENTIALS=true` + +### Проблемы с базой данных +1. Проверьте подключение к БД +2. Убедитесь, что миграции выполнены +3. Проверьте SSL настройки (`DB_SSL_MODE`) diff --git a/render.dev.yaml b/render.dev.yaml new file mode 100644 index 0000000..76b06f5 --- /dev/null +++ b/render.dev.yaml @@ -0,0 +1,70 @@ +services: + - type: web + name: strive-api-dev + env: docker + plan: free + autoDeploy: true + healthCheckPath: /health + dockerfilePath: ./Dockerfile + branch: main + envVars: + - key: PORT + value: 8080 + - key: LOG_LEVEL + value: INFO + - key: LOG_FORMAT + value: json + - key: DB_HOST + sync: false + - key: DB_PORT + value: 5432 + - key: DB_USER + sync: false + - key: DB_PASSWORD + sync: false + - key: DB_NAME + sync: false + - key: DB_SSL_MODE + value: require + - key: JWT_SECRET + sync: false + - key: JWT_ISSUER + value: strive-api + - key: JWT_AUDIENCE + value: strive-app + - key: JWT_CLOCK_SKEW + value: 2m + - key: CORS_ALLOWED_ORIGINS + value: https://your-dev-frontend.com,http://localhost:3000,http://localhost:4200 + - key: CORS_ALLOWED_METHODS + value: GET,POST,PUT,DELETE,OPTIONS + - key: CORS_ALLOWED_HEADERS + value: Accept,Authorization,Content-Type,X-Request-ID + - key: CORS_EXPOSED_HEADERS + value: X-Request-ID + - key: CORS_ALLOW_CREDENTIALS + value: "true" + - key: CORS_MAX_AGE + value: 86400 + - key: COOKIE_SECURE + value: "true" + - key: COOKIE_SAMESITE + value: "None" + - key: COOKIE_DOMAIN + value: "" + - key: RATE_LIMIT_ENABLED + value: "true" + - key: RATE_LIMIT_AUTH_PER_MINUTE + value: 10 + - key: RATE_LIMIT_GENERAL_PER_MINUTE + value: 100 + - key: RATE_LIMIT_BURST_SIZE + value: 20 + - key: EXERCISEDB_ENABLED + value: "true" + - key: EXERCISEDB_BASE_URL + value: https://exercise.hellogym.io + - key: EXERCISEDB_TIMEOUT + value: 30s + - key: EXERCISEDB_RETRY_COUNT + value: 3 diff --git a/render.yaml b/render.yaml index 1f534bb..473821a 100644 --- a/render.yaml +++ b/render.yaml @@ -6,6 +6,7 @@ services: autoDeploy: true healthCheckPath: /health dockerfilePath: ./Dockerfile + branch: main envVars: - key: PORT value: 8080 @@ -27,8 +28,56 @@ services: value: require - key: JWT_SECRET sync: false + - key: JWT_ISSUER + value: strive-api + - key: JWT_AUDIENCE + value: strive-app + - key: JWT_CLOCK_SKEW + value: 2m + - key: CORS_ALLOWED_ORIGINS + value: https://your-production-frontend.com + - key: CORS_ALLOWED_METHODS + value: GET,POST,PUT,DELETE,OPTIONS + - key: CORS_ALLOWED_HEADERS + value: Accept,Authorization,Content-Type,X-Request-ID + - key: CORS_EXPOSED_HEADERS + value: X-Request-ID + - key: CORS_ALLOW_CREDENTIALS + value: "true" + - key: CORS_MAX_AGE + value: 86400 - key: COOKIE_SECURE value: "true" - key: COOKIE_SAMESITE value: "Strict" + - key: COOKIE_DOMAIN + value: "" + - key: RATE_LIMIT_ENABLED + value: "true" + - key: RATE_LIMIT_AUTH_PER_MINUTE + value: 5 + - key: RATE_LIMIT_GENERAL_PER_MINUTE + value: 60 + - key: RATE_LIMIT_BURST_SIZE + value: 10 + - key: EXERCISEDB_ENABLED + value: "true" + - key: EXERCISEDB_BASE_URL + value: https://exercise.hellogym.io + - key: EXERCISEDB_TIMEOUT + value: 30s + - key: EXERCISEDB_RETRY_COUNT + value: 3 + - key: SECURITY_HSTS_MAX_AGE + value: 31536000 + - key: SECURITY_HSTS_INCLUDE_SUBDOMAINS + value: "true" + - key: SECURITY_X_FRAME_OPTIONS + value: DENY + - key: SECURITY_X_CONTENT_TYPE_OPTIONS + value: nosniff + - key: SECURITY_REFERRER_POLICY + value: strict-origin-when-cross-origin + - key: SECURITY_XSS_PROTECTION + value: "1; mode=block"