diff --git a/backend/Dockerfile b/backend/Dockerfile index 4705ea9..113c77f 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -36,6 +36,10 @@ COPY --chown=appuser:appgroup app/ ./app/ COPY --chown=appuser:appgroup alembic/ ./alembic/ COPY --chown=appuser:appgroup alembic.ini ./ +# Copy Infisical entrypoint +COPY --chown=appuser:appgroup entrypoint.sh ./entrypoint.sh +RUN chmod +x /app/entrypoint.sh + # Create directories for mounted volumes RUN mkdir -p /app/designs /app/config && \ chown -R appuser:appgroup /app @@ -44,4 +48,5 @@ USER appuser EXPOSE 8000 +ENTRYPOINT ["/app/entrypoint.sh"] CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"] diff --git a/backend/entrypoint.sh b/backend/entrypoint.sh new file mode 100755 index 0000000..ccc461d --- /dev/null +++ b/backend/entrypoint.sh @@ -0,0 +1,61 @@ +#!/bin/sh +# Infisical secret injection entrypoint (Python version) +set -e + +INFISICAL_URL="${INFISICAL_URL:-http://infisical:8080}" +INFISICAL_ENV="${INFISICAL_ENV:-prod}" +INFISICAL_PROJECT_SLUG="${INFISICAL_PROJECT_SLUG:-rswag}" + +if [ -z "$INFISICAL_CLIENT_ID" ] || [ -z "$INFISICAL_CLIENT_SECRET" ]; then + echo "[infisical] No credentials set, starting without secret injection" + exec "$@" +fi + +echo "[infisical] Fetching secrets from ${INFISICAL_PROJECT_SLUG}/${INFISICAL_ENV}..." + +EXPORTS=$(python3 -c " +import urllib.request, json, os, sys + +base = os.environ['INFISICAL_URL'] +slug = os.environ['INFISICAL_PROJECT_SLUG'] +env = os.environ['INFISICAL_ENV'] + +try: + data = json.dumps({'clientId': os.environ['INFISICAL_CLIENT_ID'], 'clientSecret': os.environ['INFISICAL_CLIENT_SECRET']}).encode() + req = urllib.request.Request(f'{base}/api/v1/auth/universal-auth/login', data=data, headers={'Content-Type': 'application/json'}) + auth = json.loads(urllib.request.urlopen(req).read()) + token = auth.get('accessToken') + if not token: + print('[infisical] Auth failed', file=sys.stderr) + sys.exit(1) + + req = urllib.request.Request(f'{base}/api/v3/secrets/raw?workspaceSlug={slug}&environment={env}&secretPath=/&recursive=true') + req.add_header('Authorization', f'Bearer {token}') + secrets = json.loads(urllib.request.urlopen(req).read()) + + if 'secrets' not in secrets: + print('[infisical] No secrets returned', file=sys.stderr) + sys.exit(1) + + for s in secrets['secrets']: + key = s['secretKey'] + val = s['secretValue'].replace(\"'\", \"'\\\\'\") + print(f\"export {key}='{val}'\") +except Exception as e: + print(f'[infisical] Error: {e}', file=sys.stderr) + sys.exit(1) +" 2>&1) || { + echo "[infisical] WARNING: Failed to fetch secrets, starting with existing env vars" + exec "$@" +} + +if echo "$EXPORTS" | grep -q "^export "; then + COUNT=$(echo "$EXPORTS" | grep -c "^export ") + eval "$EXPORTS" + echo "[infisical] Injected ${COUNT} secrets" +else + echo "[infisical] WARNING: $EXPORTS" + echo "[infisical] Starting with existing env vars" +fi + +exec "$@" diff --git a/docker-compose.yml b/docker-compose.yml index fabba59..0da0b9b 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -36,22 +36,16 @@ services: container_name: rswag-backend restart: unless-stopped environment: + - INFISICAL_CLIENT_ID=${INFISICAL_CLIENT_ID} + - INFISICAL_CLIENT_SECRET=${INFISICAL_CLIENT_SECRET} + - INFISICAL_PROJECT_SLUG=rswag + - INFISICAL_ENV=prod + - INFISICAL_URL=http://infisical:8080 - DATABASE_URL=postgresql://rswag:${DB_PASSWORD:-devpassword}@db:5432/rswag - REDIS_URL=redis://redis:6379 - - MOLLIE_API_KEY=${MOLLIE_API_KEY} - - PRODIGI_API_KEY=${PRODIGI_API_KEY} - - PRINTFUL_API_TOKEN=${PRINTFUL_API_TOKEN} - - POD_SANDBOX_MODE=${POD_SANDBOX_MODE:-true} - - JWT_SECRET=${JWT_SECRET:-dev-secret-change-me} - - CORS_ORIGINS=${CORS_ORIGINS:-http://localhost:3000} - DESIGNS_PATH=/app/designs - CONFIG_PATH=/app/config - SPACES_PATH=/app/spaces - - GEMINI_API_KEY=${GEMINI_API_KEY} - - FLOW_SERVICE_URL=${FLOW_SERVICE_URL:-} - - FLOW_ID=${FLOW_ID:-} - - FLOW_FUNNEL_ID=${FLOW_FUNNEL_ID:-} - - FLOW_REVENUE_SPLIT=${FLOW_REVENUE_SPLIT:-0.5} volumes: - ./designs:/app/designs - ./config:/app/config:ro