fix: Improve production deployment configuration (#155)

* fix: Improve production deployment configuration

- Update default port from 3003 to 3002 for consistency
- Make port configurable via environment variable in docker-compose
- Add .env file loading support in start-api.sh
- Optimize startup.sh for production (skip builds, expect pre-built dist)
- Make Claude Code image build conditional on Dockerfile availability
- Fix rate limiting configuration for proxy environments
- Remove jest types from tsconfig (not needed in production)

These changes improve deployment flexibility and production readiness.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>

* fix: Address PR review feedback

- Fix port inconsistency: Change hardcoded 3003 to 3002 in src/index.ts
- Fix security risk: Replace unsafe export command with set -a/source/set +a
- Remove unnecessary Dockerfile.claudecode volume mount from docker-compose
  (The Dockerfile already copies all necessary files during build)

These changes address all critical issues identified in the PR review.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>

---------

Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
Cheffromspace
2025-06-01 13:10:15 -05:00
committed by GitHub
parent af851491e8
commit 295c182351
5 changed files with 39 additions and 28 deletions

View File

@@ -2,16 +2,14 @@ services:
webhook:
build: .
ports:
- "8082:3003"
- "${PORT:-3002}:${PORT:-3002}"
volumes:
- .:/app
- /app/node_modules
- /var/run/docker.sock:/var/run/docker.sock
- ${HOME}/.aws:/root/.aws:ro
- ${HOME}/.claude-hub:/home/node/.claude
environment:
- NODE_ENV=production
- PORT=3003
- PORT=${PORT:-3002}
- TRUST_PROXY=${TRUST_PROXY:-true}
- AUTHORIZED_USERS=${AUTHORIZED_USERS:-Cheffromspace}
- BOT_USERNAME=${BOT_USERNAME:-@MCPClaude}
@@ -34,7 +32,7 @@ services:
- GITHUB_WEBHOOK_SECRET=${GITHUB_WEBHOOK_SECRET}
restart: unless-stopped
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3003/health"]
test: ["CMD", "curl", "-f", "http://localhost:${PORT:-3002}/health"]
interval: 30s
timeout: 10s
retries: 3

View File

@@ -1,7 +1,14 @@
#!/bin/bash
# Get port from environment or default to 3003
DEFAULT_PORT=${PORT:-3003}
# Load environment variables from .env file if it exists
if [ -f .env ]; then
set -a
source .env
set +a
fi
# Get port from environment or default to 3002
DEFAULT_PORT=${PORT:-3002}
# Kill any processes using the port
echo "Checking for existing processes on port $DEFAULT_PORT..."

View File

@@ -2,24 +2,24 @@
echo "Starting Claude GitHub webhook service..."
# Build the Claude Code runner image
echo "Building Claude Code runner image..."
if docker build -f Dockerfile.claudecode -t claude-code-runner:latest .; then
echo "Claude Code runner image built successfully."
# Build the Claude Code runner image if we have access to Dockerfile.claudecode
if [ -f "Dockerfile.claudecode" ]; then
echo "Building Claude Code runner image..."
if docker build -f Dockerfile.claudecode -t claude-code-runner:latest .; then
echo "Claude Code runner image built successfully."
else
echo "Warning: Failed to build Claude Code runner image. Service will attempt to build on first use."
fi
else
echo "Warning: Failed to build Claude Code runner image. Service will attempt to build on first use."
echo "Dockerfile.claudecode not found, skipping Claude Code runner image build."
fi
# Ensure dependencies are installed (in case volume mount affected node_modules)
if [ ! -d "node_modules" ] || [ ! -f "node_modules/.bin/tsc" ]; then
echo "Installing dependencies..."
npm ci
# In production, dist directory is already built in the Docker image
if [ ! -d "dist" ]; then
echo "Error: dist directory not found. Please rebuild the Docker image."
exit 1
fi
# Always compile TypeScript to ensure we have the latest compiled source
echo "Compiling TypeScript..."
npm run build
# Start the webhook service
echo "Starting webhook service..."
exec node dist/index.js

View File

@@ -18,7 +18,7 @@ if (trustProxy) {
app.set('trust proxy', true);
}
const PORT = parseInt(process.env['PORT'] ?? '3003', 10);
const PORT = parseInt(process.env['PORT'] ?? '3002', 10);
const appLogger = createLogger('app');
const startupMetrics = new StartupMetrics();
@@ -27,26 +27,32 @@ startupMetrics.recordMilestone('env_loaded', 'Environment variables loaded');
startupMetrics.recordMilestone('express_initialized', 'Express app initialized');
// Rate limiting configuration
const generalRateLimit = rateLimit({
// When behind a proxy, we need to properly handle client IP detection
const rateLimitConfig = {
windowMs: 15 * 60 * 1000, // 15 minutes
standardHeaders: true, // Return rate limit info in the `RateLimit-*` headers
legacyHeaders: false, // Disable the `X-RateLimit-*` headers
// Skip validation when behind proxy to avoid startup errors
validate: trustProxy ? false : undefined
};
const generalRateLimit = rateLimit({
...rateLimitConfig,
max: 100, // Limit each IP to 100 requests per windowMs
message: {
error: 'Too many requests',
message: 'Too many requests from this IP, please try again later.'
},
standardHeaders: true, // Return rate limit info in the `RateLimit-*` headers
legacyHeaders: false // Disable the `X-RateLimit-*` headers
}
});
const webhookRateLimit = rateLimit({
...rateLimitConfig,
windowMs: 5 * 60 * 1000, // 5 minutes
max: 50, // Limit each IP to 50 webhook requests per 5 minutes
message: {
error: 'Too many webhook requests',
message: 'Too many webhook requests from this IP, please try again later.'
},
standardHeaders: true,
legacyHeaders: false,
skip: _req => {
// Skip rate limiting in test environment
return process.env['NODE_ENV'] === 'test';

View File

@@ -29,7 +29,7 @@
"noFallthroughCasesInSwitch": true,
"noErrorTruncation": true,
"isolatedModules": true,
"types": ["node", "jest"]
"types": ["node"]
},
"include": [
"src/**/*"