forked from claude-did-this/claude-hub
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:
@@ -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
|
||||
|
||||
@@ -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..."
|
||||
|
||||
@@ -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
|
||||
20
src/index.ts
20
src/index.ts
@@ -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';
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"noErrorTruncation": true,
|
||||
"isolatedModules": true,
|
||||
"types": ["node", "jest"]
|
||||
"types": ["node"]
|
||||
},
|
||||
"include": [
|
||||
"src/**/*"
|
||||
|
||||
Reference in New Issue
Block a user