diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..482c63d --- /dev/null +++ b/.env.example @@ -0,0 +1,36 @@ +# MotoVaultPro Environment Configuration +# Copy to .env and fill in environment-specific values +# Generated .env files should NOT be committed to version control +# +# Local dev: No .env needed -- base docker-compose.yml defaults are sandbox values +# Staging/Production: CI/CD generates .env from Gitea variables + generate-log-config.sh + +# =========================================== +# Stripe Price IDs (environment-specific) +# =========================================== +# Sandbox defaults used for local development +STRIPE_PRO_MONTHLY_PRICE_ID=price_1T1ZHMJXoKkh5RcKwKSSGIlR +STRIPE_PRO_YEARLY_PRICE_ID=price_1T1ZHnJXoKkh5RcKWlG2MPpX +STRIPE_ENTERPRISE_MONTHLY_PRICE_ID=price_1T1ZIBJXoKkh5RcKu2jyhqBN +STRIPE_ENTERPRISE_YEARLY_PRICE_ID=price_1T1ZIQJXoKkh5RcK34YXiJQm + +# =========================================== +# Stripe Publishable Key (baked into frontend at build time) +# =========================================== +# VITE_STRIPE_PUBLISHABLE_KEY=pk_test_... + +# =========================================== +# Log Levels (generated by scripts/ci/generate-log-config.sh) +# =========================================== +# Run: ./scripts/ci/generate-log-config.sh DEBUG >> .env +# +# BACKEND_LOG_LEVEL=debug +# TRAEFIK_LOG_LEVEL=DEBUG +# POSTGRES_LOG_STATEMENT=all +# POSTGRES_LOG_MIN_DURATION=0 +# REDIS_LOGLEVEL=debug + +# =========================================== +# Grafana +# =========================================== +# GRAFANA_ADMIN_PASSWORD=admin diff --git a/.gitea/workflows/production.yaml b/.gitea/workflows/production.yaml index e44722b..9719752 100644 --- a/.gitea/workflows/production.yaml +++ b/.gitea/workflows/production.yaml @@ -99,6 +99,7 @@ jobs: docker-compose.yml docker-compose.blue-green.yml docker-compose.prod.yml + .env.example sparse-checkout-cone-mode: false fetch-depth: 1 @@ -115,11 +116,20 @@ jobs: mkdir -p "$DEPLOY_PATH/secrets/app" cp "$GITHUB_WORKSPACE/secrets/app/google-wif-config.json" "$DEPLOY_PATH/secrets/app/" - - name: Generate logging configuration + - name: Generate environment configuration run: | cd "$DEPLOY_PATH" + { + echo "# Generated by CI/CD - DO NOT EDIT" + echo "STRIPE_PRO_MONTHLY_PRICE_ID=${{ vars.STRIPE_PRO_MONTHLY_PRICE_ID }}" + echo "STRIPE_PRO_YEARLY_PRICE_ID=${{ vars.STRIPE_PRO_YEARLY_PRICE_ID }}" + echo "STRIPE_ENTERPRISE_MONTHLY_PRICE_ID=${{ vars.STRIPE_ENTERPRISE_MONTHLY_PRICE_ID }}" + echo "STRIPE_ENTERPRISE_YEARLY_PRICE_ID=${{ vars.STRIPE_ENTERPRISE_YEARLY_PRICE_ID }}" + echo "VITE_STRIPE_PUBLISHABLE_KEY=${{ vars.VITE_STRIPE_PUBLISHABLE_KEY }}" + echo "GRAFANA_ADMIN_PASSWORD=${{ secrets.GRAFANA_ADMIN_PASSWORD }}" + } > .env chmod +x scripts/ci/generate-log-config.sh - ./scripts/ci/generate-log-config.sh "$LOG_LEVEL" + ./scripts/ci/generate-log-config.sh "$LOG_LEVEL" >> .env - name: Login to registry run: | diff --git a/.gitea/workflows/staging.yaml b/.gitea/workflows/staging.yaml index 51fb774..8f140e8 100644 --- a/.gitea/workflows/staging.yaml +++ b/.gitea/workflows/staging.yaml @@ -124,11 +124,20 @@ jobs: mkdir -p "$DEPLOY_PATH/secrets/app" cp "$GITHUB_WORKSPACE/secrets/app/google-wif-config.json" "$DEPLOY_PATH/secrets/app/" - - name: Generate logging configuration + - name: Generate environment configuration run: | cd "$DEPLOY_PATH" + { + echo "# Generated by CI/CD - DO NOT EDIT" + echo "STRIPE_PRO_MONTHLY_PRICE_ID=${{ vars.STRIPE_PRO_MONTHLY_PRICE_ID }}" + echo "STRIPE_PRO_YEARLY_PRICE_ID=${{ vars.STRIPE_PRO_YEARLY_PRICE_ID }}" + echo "STRIPE_ENTERPRISE_MONTHLY_PRICE_ID=${{ vars.STRIPE_ENTERPRISE_MONTHLY_PRICE_ID }}" + echo "STRIPE_ENTERPRISE_YEARLY_PRICE_ID=${{ vars.STRIPE_ENTERPRISE_YEARLY_PRICE_ID }}" + echo "VITE_STRIPE_PUBLISHABLE_KEY=${{ vars.VITE_STRIPE_PUBLISHABLE_KEY }}" + echo "GRAFANA_ADMIN_PASSWORD=${{ secrets.GRAFANA_ADMIN_PASSWORD }}" + } > .env chmod +x scripts/ci/generate-log-config.sh - ./scripts/ci/generate-log-config.sh "$LOG_LEVEL" + ./scripts/ci/generate-log-config.sh "$LOG_LEVEL" >> .env - name: Login to registry run: | diff --git a/docker-compose.blue-green.yml b/docker-compose.blue-green.yml index 28bf11a..325512f 100644 --- a/docker-compose.blue-green.yml +++ b/docker-compose.blue-green.yml @@ -11,6 +11,63 @@ # Shared services (from base compose): # mvp-traefik, mvp-postgres, mvp-redis +# ======================================== +# Extension fields (YAML anchors for DRY) +# ======================================== +x-frontend-env: &frontend-env + VITE_API_BASE_URL: /api + VITE_AUTH0_DOMAIN: ${VITE_AUTH0_DOMAIN:-motovaultpro.us.auth0.com} + VITE_AUTH0_CLIENT_ID: ${VITE_AUTH0_CLIENT_ID:-yspR8zdnSxmV8wFIghHynQ08iXAPoQJ3} + VITE_AUTH0_AUDIENCE: ${VITE_AUTH0_AUDIENCE:-https://api.motovaultpro.com} + SECRETS_DIR: /run/secrets + +x-frontend-volumes: &frontend-volumes + - ./secrets/app/google-maps-api-key.txt:/run/secrets/google-maps-api-key:ro + - ./secrets/app/google-maps-map-id.txt:/run/secrets/google-maps-map-id:ro + +x-frontend-healthcheck: &frontend-healthcheck + test: ["CMD-SHELL", "curl -sf http://localhost:3000 || exit 1"] + interval: 5s + timeout: 5s + retries: 3 + start_period: 10s + +x-backend-env: &backend-env + NODE_ENV: production + CONFIG_PATH: /app/config/production.yml + SECRETS_DIR: /run/secrets + LOG_LEVEL: ${BACKEND_LOG_LEVEL:-debug} + DATABASE_HOST: mvp-postgres + REDIS_HOST: mvp-redis + STRIPE_PRO_MONTHLY_PRICE_ID: ${STRIPE_PRO_MONTHLY_PRICE_ID:-price_1T1ZHMJXoKkh5RcKwKSSGIlR} + STRIPE_PRO_YEARLY_PRICE_ID: ${STRIPE_PRO_YEARLY_PRICE_ID:-price_1T1ZHnJXoKkh5RcKWlG2MPpX} + STRIPE_ENTERPRISE_MONTHLY_PRICE_ID: ${STRIPE_ENTERPRISE_MONTHLY_PRICE_ID:-price_1T1ZIBJXoKkh5RcKu2jyhqBN} + STRIPE_ENTERPRISE_YEARLY_PRICE_ID: ${STRIPE_ENTERPRISE_YEARLY_PRICE_ID:-price_1T1ZIQJXoKkh5RcK34YXiJQm} + +x-backend-volumes: &backend-volumes + - ./config/app/production.yml:/app/config/production.yml:ro + - ./config/shared/production.yml:/app/config/shared.yml:ro + - ./secrets/app/postgres-password.txt:/run/secrets/postgres-password:ro + - ./secrets/app/auth0-client-secret.txt:/run/secrets/auth0-client-secret:ro + - ./secrets/app/google-maps-api-key.txt:/run/secrets/google-maps-api-key:ro + - ./secrets/app/google-maps-map-id.txt:/run/secrets/google-maps-map-id:ro + - ./secrets/app/resend-api-key.txt:/run/secrets/resend-api-key:ro + - ./secrets/app/auth0-management-client-id.txt:/run/secrets/auth0-management-client-id:ro + - ./secrets/app/auth0-management-client-secret.txt:/run/secrets/auth0-management-client-secret:ro + - ./secrets/app/stripe-secret-key.txt:/run/secrets/stripe-secret-key:ro + - ./secrets/app/stripe-webhook-secret.txt:/run/secrets/stripe-webhook-secret:ro + - ./data/documents:/app/data/documents + - ./data/backups:/app/data/backups + +x-backend-healthcheck: &backend-healthcheck + test: + - CMD-SHELL + - node -e "require('http').get('http://localhost:3001/health', r => process.exit(r.statusCode===200?0:1)).on('error', () => process.exit(1))" + interval: 5s + timeout: 5s + retries: 5 + start_period: 180s + services: # ======================================== # BLUE Stack - Frontend @@ -19,25 +76,13 @@ services: image: ${FRONTEND_IMAGE:-git.motovaultpro.com/egullickson/frontend:latest} container_name: mvp-frontend-blue restart: unless-stopped - environment: - VITE_API_BASE_URL: /api - VITE_AUTH0_DOMAIN: ${VITE_AUTH0_DOMAIN:-motovaultpro.us.auth0.com} - VITE_AUTH0_CLIENT_ID: ${VITE_AUTH0_CLIENT_ID:-yspR8zdnSxmV8wFIghHynQ08iXAPoQJ3} - VITE_AUTH0_AUDIENCE: ${VITE_AUTH0_AUDIENCE:-https://api.motovaultpro.com} - SECRETS_DIR: /run/secrets - volumes: - - ./secrets/app/google-maps-api-key.txt:/run/secrets/google-maps-api-key:ro - - ./secrets/app/google-maps-map-id.txt:/run/secrets/google-maps-map-id:ro + environment: *frontend-env + volumes: *frontend-volumes networks: - frontend depends_on: - mvp-backend-blue - healthcheck: - test: ["CMD-SHELL", "curl -sf http://localhost:3000 || exit 1"] - interval: 5s - timeout: 5s - retries: 3 - start_period: 10s + healthcheck: *frontend-healthcheck deploy: resources: limits: @@ -55,50 +100,15 @@ services: image: ${BACKEND_IMAGE:-git.motovaultpro.com/egullickson/backend:latest} container_name: mvp-backend-blue restart: unless-stopped - environment: - NODE_ENV: production - CONFIG_PATH: /app/config/production.yml - SECRETS_DIR: /run/secrets - DATABASE_HOST: mvp-postgres - REDIS_HOST: mvp-redis - # Production Variables - #STRIPE_PRO_MONTHLY_PRICE_ID: prod_Toj6BG9Z9JwREl - #STRIPE_PRO_YEARLY_PRICE_ID: prod_Toj8oo0RpVBQmB - #STRIPE_ENTERPRISE_MONTHLY_PRICE_ID: prod_Toj8xGEui9jl6j - #STRIPE_ENTERPRISE_YEARLY_PRICE_ID: prod_Toj9A7A773xrdn - # Sandbox Variables - STRIPE_PRO_MONTHLY_PRICE_ID: price_1T1ZHMJXoKkh5RcKwKSSGIlR - STRIPE_PRO_YEARLY_PRICE_ID: price_1T1ZHnJXoKkh5RcKWlG2MPpX - STRIPE_ENTERPRISE_MONTHLY_PRICE_ID: price_1T1ZIBJXoKkh5RcKu2jyhqBN - STRIPE_ENTERPRISE_YEARLY_PRICE_ID: price_1T1ZIQJXoKkh5RcK34YXiJQm - volumes: - - ./config/app/production.yml:/app/config/production.yml:ro - - ./config/shared/production.yml:/app/config/shared.yml:ro - - ./secrets/app/postgres-password.txt:/run/secrets/postgres-password:ro - - ./secrets/app/auth0-client-secret.txt:/run/secrets/auth0-client-secret:ro - - ./secrets/app/google-maps-api-key.txt:/run/secrets/google-maps-api-key:ro - - ./secrets/app/google-maps-map-id.txt:/run/secrets/google-maps-map-id:ro - - ./secrets/app/resend-api-key.txt:/run/secrets/resend-api-key:ro - - ./secrets/app/auth0-management-client-id.txt:/run/secrets/auth0-management-client-id:ro - - ./secrets/app/auth0-management-client-secret.txt:/run/secrets/auth0-management-client-secret:ro - - ./secrets/app/stripe-secret-key.txt:/run/secrets/stripe-secret-key:ro - - ./secrets/app/stripe-webhook-secret.txt:/run/secrets/stripe-webhook-secret:ro - - ./data/documents:/app/data/documents - - ./data/backups:/app/data/backups + environment: *backend-env + volumes: *backend-volumes networks: - backend - database depends_on: - mvp-postgres - mvp-redis - healthcheck: - test: - - CMD-SHELL - - node -e "require('http').get('http://localhost:3001/health', r => process.exit(r.statusCode===200?0:1)).on('error', () => process.exit(1))" - interval: 5s - timeout: 5s - retries: 5 - start_period: 180s + healthcheck: *backend-healthcheck deploy: resources: limits: @@ -116,25 +126,13 @@ services: image: ${FRONTEND_IMAGE:-git.motovaultpro.com/egullickson/frontend:latest} container_name: mvp-frontend-green restart: unless-stopped - environment: - VITE_API_BASE_URL: /api - VITE_AUTH0_DOMAIN: ${VITE_AUTH0_DOMAIN:-motovaultpro.us.auth0.com} - VITE_AUTH0_CLIENT_ID: ${VITE_AUTH0_CLIENT_ID:-yspR8zdnSxmV8wFIghHynQ08iXAPoQJ3} - VITE_AUTH0_AUDIENCE: ${VITE_AUTH0_AUDIENCE:-https://api.motovaultpro.com} - SECRETS_DIR: /run/secrets - volumes: - - ./secrets/app/google-maps-api-key.txt:/run/secrets/google-maps-api-key:ro - - ./secrets/app/google-maps-map-id.txt:/run/secrets/google-maps-map-id:ro + environment: *frontend-env + volumes: *frontend-volumes networks: - frontend depends_on: - mvp-backend-green - healthcheck: - test: ["CMD-SHELL", "curl -sf http://localhost:3000 || exit 1"] - interval: 5s - timeout: 5s - retries: 3 - start_period: 10s + healthcheck: *frontend-healthcheck deploy: resources: limits: @@ -152,51 +150,15 @@ services: image: ${BACKEND_IMAGE:-git.motovaultpro.com/egullickson/backend:latest} container_name: mvp-backend-green restart: unless-stopped - environment: - NODE_ENV: production - CONFIG_PATH: /app/config/production.yml - SECRETS_DIR: /run/secrets - DATABASE_HOST: mvp-postgres - REDIS_HOST: mvp-redis - # Production Variables - #STRIPE_PRO_MONTHLY_PRICE_ID: prod_Toj6BG9Z9JwREl - #STRIPE_PRO_YEARLY_PRICE_ID: prod_Toj8oo0RpVBQmB - #STRIPE_ENTERPRISE_MONTHLY_PRICE_ID: prod_Toj8xGEui9jl6j - #STRIPE_ENTERPRISE_YEARLY_PRICE_ID: prod_Toj9A7A773xrdn - # Sandbox Variables - STRIPE_PRO_MONTHLY_PRICE_ID: price_1T1ZHMJXoKkh5RcKwKSSGIlR - STRIPE_PRO_YEARLY_PRICE_ID: price_1T1ZHnJXoKkh5RcKWlG2MPpX - STRIPE_ENTERPRISE_MONTHLY_PRICE_ID: price_1T1ZIBJXoKkh5RcKu2jyhqBN - STRIPE_ENTERPRISE_YEARLY_PRICE_ID: price_1T1ZIQJXoKkh5RcK34YXiJQm - - volumes: - - ./config/app/production.yml:/app/config/production.yml:ro - - ./config/shared/production.yml:/app/config/shared.yml:ro - - ./secrets/app/postgres-password.txt:/run/secrets/postgres-password:ro - - ./secrets/app/auth0-client-secret.txt:/run/secrets/auth0-client-secret:ro - - ./secrets/app/google-maps-api-key.txt:/run/secrets/google-maps-api-key:ro - - ./secrets/app/google-maps-map-id.txt:/run/secrets/google-maps-map-id:ro - - ./secrets/app/resend-api-key.txt:/run/secrets/resend-api-key:ro - - ./secrets/app/auth0-management-client-id.txt:/run/secrets/auth0-management-client-id:ro - - ./secrets/app/auth0-management-client-secret.txt:/run/secrets/auth0-management-client-secret:ro - - ./secrets/app/stripe-secret-key.txt:/run/secrets/stripe-secret-key:ro - - ./secrets/app/stripe-webhook-secret.txt:/run/secrets/stripe-webhook-secret:ro - - ./data/documents:/app/data/documents - - ./data/backups:/app/data/backups + environment: *backend-env + volumes: *backend-volumes networks: - backend - database depends_on: - mvp-postgres - mvp-redis - healthcheck: - test: - - CMD-SHELL - - node -e "require('http').get('http://localhost:3001/health', r => process.exit(r.statusCode===200?0:1)).on('error', () => process.exit(1))" - interval: 5s - timeout: 5s - retries: 5 - start_period: 180s + healthcheck: *backend-healthcheck deploy: resources: limits: diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml index 47e2bd4..cd693f5 100644 --- a/docker-compose.prod.yml +++ b/docker-compose.prod.yml @@ -6,18 +6,14 @@ # # This file removes development-only configurations: # - Database port exposure (PostgreSQL, Redis) -# - Development-specific settings +# - Traefik dashboard auth middleware +# +# Environment-specific values (log levels, Stripe IDs) are driven by .env +# generated by CI/CD from Gitea variables + scripts/ci/generate-log-config.sh services: - # Traefik - Production log level and dashboard auth + # Traefik - Dashboard auth middleware mvp-traefik: - environment: - # Traefik log levels: TRACE | DEBUG | INFO | WARN | ERROR - LOG_LEVEL: error - command: - - --configFile=/etc/traefik/traefik.yml - # Traefik log levels: TRACE | DEBUG | INFO | WARN | ERROR - - --log.level=ERROR labels: - "traefik.enable=true" - "traefik.http.routers.traefik-dashboard.rule=Host(`traefik.motovaultpro.local`)" @@ -26,64 +22,10 @@ services: - "traefik.http.services.traefik-dashboard.loadbalancer.server.port=8080" - "traefik.http.middlewares.dashboard-auth.basicauth.users=admin:$$2y$$10$$foobar" - # Backend - Production log level - mvp-backend: - environment: - NODE_ENV: production - CONFIG_PATH: /app/config/production.yml - SECRETS_DIR: /run/secrets - # Pino log levels: trace | debug | info | warn | error | fatal - LOG_LEVEL: error - DATABASE_HOST: mvp-postgres - REDIS_HOST: mvp-redis - # Production Variables - #STRIPE_PRO_MONTHLY_PRICE_ID: prod_Toj6BG9Z9JwREl - #STRIPE_PRO_YEARLY_PRICE_ID: prod_Toj8oo0RpVBQmB - #STRIPE_ENTERPRISE_MONTHLY_PRICE_ID: prod_Toj8xGEui9jl6j - #STRIPE_ENTERPRISE_YEARLY_PRICE_ID: prod_Toj9A7A773xrdn - # Sandbox Variables - STRIPE_PRO_MONTHLY_PRICE_ID: price_1T1ZHMJXoKkh5RcKwKSSGIlR - STRIPE_PRO_YEARLY_PRICE_ID: price_1T1ZHnJXoKkh5RcKWlG2MPpX - STRIPE_ENTERPRISE_MONTHLY_PRICE_ID: price_1T1ZIBJXoKkh5RcKu2jyhqBN - STRIPE_ENTERPRISE_YEARLY_PRICE_ID: price_1T1ZIQJXoKkh5RcK34YXiJQm - - # OCR - Production log level + engine config - mvp-ocr: - environment: - # Python log levels: DEBUG | INFO | WARNING | ERROR | CRITICAL - LOG_LEVEL: error - REDIS_HOST: mvp-redis - REDIS_PORT: 6379 - REDIS_DB: 1 - # OCR engine configuration (Google Vision primary, PaddleOCR fallback) - OCR_PRIMARY_ENGINE: google_vision - OCR_FALLBACK_ENGINE: paddleocr - OCR_CONFIDENCE_THRESHOLD: "0.6" - OCR_FALLBACK_THRESHOLD: "0.6" - GOOGLE_VISION_KEY_PATH: /run/secrets/google-wif-config.json - VISION_MONTHLY_LIMIT: "1000" - # Vertex AI / Gemini configuration (maintenance schedule extraction) - VERTEX_AI_PROJECT: motovaultpro - VERTEX_AI_LOCATION: us-central1 - GEMINI_MODEL: gemini-2.5-flash - - # PostgreSQL - Remove dev ports, production log level + # PostgreSQL - Remove dev ports mvp-postgres: ports: [] - environment: - POSTGRES_DB: motovaultpro - POSTGRES_USER: postgres - POSTGRES_PASSWORD_FILE: /run/secrets/postgres-password - POSTGRES_INITDB_ARGS: --encoding=UTF8 - LOG_LEVEL: error - # PostgreSQL log statements: none | ddl | mod | all - POSTGRES_LOG_STATEMENT: none - # Minimum query duration to log: -1 (disabled) | 0 (all) | N (ms threshold) - POSTGRES_LOG_MIN_DURATION_STATEMENT: -1 - PGDATA: /var/lib/postgresql/data/pgdata - # Redis - Remove dev ports, production log level + # Redis - Remove dev ports mvp-redis: ports: [] - # Redis log levels: debug | verbose | notice | warning - command: redis-server --appendonly yes --loglevel warning diff --git a/docker-compose.staging.yml b/docker-compose.staging.yml index e6395aa..c97021f 100644 --- a/docker-compose.staging.yml +++ b/docker-compose.staging.yml @@ -63,27 +63,6 @@ services: mvp-ocr: image: ${OCR_IMAGE:-git.motovaultpro.com/egullickson/ocr:latest} container_name: mvp-ocr-staging - environment: - # Python log levels: DEBUG | INFO | WARNING | ERROR | CRITICAL - LOG_LEVEL: debug - REDIS_HOST: mvp-redis - REDIS_PORT: 6379 - REDIS_DB: 1 - # OCR engine configuration (Google Vision primary, PaddleOCR fallback) - OCR_PRIMARY_ENGINE: google_vision - OCR_FALLBACK_ENGINE: paddleocr - OCR_CONFIDENCE_THRESHOLD: "0.6" - OCR_FALLBACK_THRESHOLD: "0.6" - GOOGLE_VISION_KEY_PATH: /run/secrets/google-wif-config.json - VISION_MONTHLY_LIMIT: "1000" - # Vertex AI / Gemini configuration (maintenance schedule extraction) - VERTEX_AI_PROJECT: motovaultpro - VERTEX_AI_LOCATION: us-central1 - GEMINI_MODEL: gemini-2.5-flash - volumes: - - ./secrets/app/auth0-ocr-client-id.txt:/run/secrets/auth0-ocr-client-id:ro - - ./secrets/app/auth0-ocr-client-secret.txt:/run/secrets/auth0-ocr-client-secret:ro - - ./secrets/app/google-wif-config.json:/run/secrets/google-wif-config.json:ro # ======================================== # PostgreSQL (Staging - Separate Database) diff --git a/docker-compose.yml b/docker-compose.yml index f9dd6bf..ed526ce 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -11,8 +11,9 @@ services: command: - --configFile=/etc/traefik/traefik.yml environment: - # Traefik log levels: TRACE | DEBUG | INFO | WARN | ERROR - LOG_LEVEL: debug + # Traefik natively reads TRAEFIK_LOG_LEVEL (maps to --log.level) + # Levels: TRACE | DEBUG | INFO | WARN | ERROR + TRAEFIK_LOG_LEVEL: ${TRAEFIK_LOG_LEVEL:-DEBUG} CLOUDFLARE_DNS_API_TOKEN_FILE: /run/secrets/cloudflare-dns-token ports: - "80:80" @@ -60,7 +61,7 @@ services: VITE_AUTH0_CLIENT_ID: ${VITE_AUTH0_CLIENT_ID:-yspR8zdnSxmV8wFIghHynQ08iXAPoQJ3} VITE_AUTH0_AUDIENCE: ${VITE_AUTH0_AUDIENCE:-https://api.motovaultpro.com} VITE_API_BASE_URL: ${VITE_API_BASE_URL:-/api} - VITE_STRIPE_PUBLISHABLE_KEY: ${VITE_STRIPE_PUBLISHABLE_KEY:-pk_live_51Sr2yQJk87CpWj04YNBIaUWUtnJjeVTgk5NqHdpjqxgsbjy3dMKkIsqhjcpSkCzp3KvLi23BGgxhwV021EnEW3H400HhPYVyfN} + VITE_STRIPE_PUBLISHABLE_KEY: ${VITE_STRIPE_PUBLISHABLE_KEY} container_name: mvp-frontend restart: unless-stopped environment: @@ -115,20 +116,15 @@ services: CONFIG_PATH: /app/config/production.yml SECRETS_DIR: /run/secrets # Pino log levels: trace | debug | info | warn | error | fatal - LOG_LEVEL: debug + LOG_LEVEL: ${BACKEND_LOG_LEVEL:-debug} # Service references DATABASE_HOST: mvp-postgres REDIS_HOST: mvp-redis - # Production Variables - #STRIPE_PRO_MONTHLY_PRICE_ID: prod_Toj6BG9Z9JwREl - #STRIPE_PRO_YEARLY_PRICE_ID: prod_Toj8oo0RpVBQmB - #STRIPE_ENTERPRISE_MONTHLY_PRICE_ID: prod_Toj8xGEui9jl6j - #STRIPE_ENTERPRISE_YEARLY_PRICE_ID: prod_Toj9A7A773xrdn - # Sandbox Variables - STRIPE_PRO_MONTHLY_PRICE_ID: price_1T1ZHMJXoKkh5RcKwKSSGIlR - STRIPE_PRO_YEARLY_PRICE_ID: price_1T1ZHnJXoKkh5RcKWlG2MPpX - STRIPE_ENTERPRISE_MONTHLY_PRICE_ID: price_1T1ZIBJXoKkh5RcKu2jyhqBN - STRIPE_ENTERPRISE_YEARLY_PRICE_ID: price_1T1ZIQJXoKkh5RcK34YXiJQm + # Stripe Price IDs (override via .env for staging/production) + STRIPE_PRO_MONTHLY_PRICE_ID: ${STRIPE_PRO_MONTHLY_PRICE_ID:-price_1T1ZHMJXoKkh5RcKwKSSGIlR} + STRIPE_PRO_YEARLY_PRICE_ID: ${STRIPE_PRO_YEARLY_PRICE_ID:-price_1T1ZHnJXoKkh5RcKWlG2MPpX} + STRIPE_ENTERPRISE_MONTHLY_PRICE_ID: ${STRIPE_ENTERPRISE_MONTHLY_PRICE_ID:-price_1T1ZIBJXoKkh5RcKu2jyhqBN} + STRIPE_ENTERPRISE_YEARLY_PRICE_ID: ${STRIPE_ENTERPRISE_YEARLY_PRICE_ID:-price_1T1ZIQJXoKkh5RcK34YXiJQm} volumes: # Configuration files (K8s ConfigMap equivalent) - ./config/app/production.yml:/app/config/production.yml:ro @@ -197,7 +193,7 @@ services: restart: unless-stopped environment: # Python log levels: DEBUG | INFO | WARNING | ERROR | CRITICAL - LOG_LEVEL: debug + LOG_LEVEL: ${BACKEND_LOG_LEVEL:-debug} REDIS_HOST: mvp-redis REDIS_PORT: 6379 REDIS_DB: 1 @@ -244,11 +240,11 @@ services: POSTGRES_USER: postgres POSTGRES_PASSWORD_FILE: /run/secrets/postgres-password POSTGRES_INITDB_ARGS: --encoding=UTF8 - LOG_LEVEL: debug + LOG_LEVEL: ${BACKEND_LOG_LEVEL:-debug} # PostgreSQL log statements: none | ddl | mod | all - POSTGRES_LOG_STATEMENT: all + POSTGRES_LOG_STATEMENT: ${POSTGRES_LOG_STATEMENT:-all} # Minimum query duration to log: -1 (disabled) | 0 (all) | N (ms threshold) - POSTGRES_LOG_MIN_DURATION_STATEMENT: 0 + POSTGRES_LOG_MIN_DURATION_STATEMENT: ${POSTGRES_LOG_MIN_DURATION:-0} PGDATA: /var/lib/postgresql/data/pgdata volumes: - mvp_postgres_data:/var/lib/postgresql/data/pgdata @@ -276,7 +272,7 @@ services: container_name: mvp-redis restart: unless-stopped # Redis log levels: debug | verbose | notice | warning - command: redis-server --appendonly yes --loglevel debug + command: redis-server --appendonly yes --loglevel ${REDIS_LOGLEVEL:-debug} volumes: - mvp_redis_data:/data networks: diff --git a/scripts/ci/generate-log-config.sh b/scripts/ci/generate-log-config.sh index 4424094..c7ab172 100755 --- a/scripts/ci/generate-log-config.sh +++ b/scripts/ci/generate-log-config.sh @@ -1,11 +1,12 @@ #!/bin/bash -# generate-log-config.sh - Generate .env.logging from LOG_LEVEL -# Maps a single LOG_LEVEL environment variable to per-container settings +# generate-log-config.sh - Generate log-level environment variables +# Maps a single LOG_LEVEL to per-container settings and writes to stdout # # Usage: ./generate-log-config.sh [LOG_LEVEL] # LOG_LEVEL: DEBUG, INFO, WARN, or ERROR (default: INFO) # -# Output: Creates .env.logging file with container-specific log settings +# Output: Log configuration variables on stdout (append to .env) +# Example: ./generate-log-config.sh INFO >> .env # # Exit codes: # 0 - Configuration generated successfully @@ -43,27 +44,13 @@ case "$LOG_LEVEL" in ERROR) REDIS_LOGLEVEL="warning" ;; esac -# Generate .env.logging file -cat > .env.logging << EOF -# Generated by generate-log-config.sh - DO NOT EDIT MANUALLY -# Regenerate with: ./scripts/ci/generate-log-config.sh $LOG_LEVEL -LOG_LEVEL=$LOG_LEVEL +# Output log configuration to stdout +cat << EOF -# Backend/OCR (Pino) +# Log levels (generated by generate-log-config.sh $LOG_LEVEL) BACKEND_LOG_LEVEL=$LOG_LEVEL_LOWER - -# Frontend (Vite) -VITE_LOG_LEVEL=$LOG_LEVEL_LOWER - -# PostgreSQL +TRAEFIK_LOG_LEVEL=$LOG_LEVEL POSTGRES_LOG_STATEMENT=$POSTGRES_LOG_STATEMENT POSTGRES_LOG_MIN_DURATION=$POSTGRES_LOG_MIN_DURATION - -# Redis REDIS_LOGLEVEL=$REDIS_LOGLEVEL - -# Traefik -TRAEFIK_LOG_LEVEL=$LOG_LEVEL EOF - -echo "Generated .env.logging with LOG_LEVEL=$LOG_LEVEL"