From 602452b162e328bd811f8f315c84fdf9bbb8e288 Mon Sep 17 00:00:00 2001 From: Arseniy Ivanov Date: Sun, 3 Aug 2025 17:15:09 -0400 Subject: [PATCH 1/2] Add Docker support with comprehensive deployment options MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add multi-stage Dockerfile with optimized builds for Go backend and Node.js frontend - Create docker-entrypoint.sh script for managing both services with PM2 - Add .dockerignore for optimal build context - Update README.md with Docker deployment documentation including: - Docker build and run instructions - Persistent data configuration with volume mounts - Complete environment variable reference table - Docker Compose example configuration 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- .dockerignore | 83 +++++++++++++++++++++++++++++ Dockerfile | 85 ++++++++++++++++++++++++++++++ README.md | 88 +++++++++++++++++++++++++++++-- docker-entrypoint.sh | 122 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 375 insertions(+), 3 deletions(-) create mode 100644 .dockerignore create mode 100644 Dockerfile create mode 100644 docker-entrypoint.sh diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..ac2e52d --- /dev/null +++ b/.dockerignore @@ -0,0 +1,83 @@ +# Docker ignore file for Claude Code Proxy +# Excludes unnecessary files from Docker build context + +# Git +.git +.gitignore +.gitattributes + +# Docker +Dockerfile +.dockerignore +docker-compose*.yml + +# Documentation +README.md +*.md +demo.gif + +# Environment and config +.env +.env.local +.env.example + +# Build artifacts +bin/ +build/ +dist/ +target/ + +# Dependencies +node_modules/ +vendor/ + +# Cache and temporary files +.cache/ +.temp/ +.tmp/ +*.tmp +*.log + +# IDE and editor files +.vscode/ +.idea/ +*.swp +*.swo +*~ + +# OS generated files +.DS_Store +.DS_Store? +._* +.Spotlight-V100 +.Trashes +ehthumbs.db +Thumbs.db + +# Database files (will be created in container) +*.db +*.sqlite +*.sqlite3 +requests/ + +# Web specific +web/.cache/ +web/build/ +web/dist/ +web/node_modules/ +web/npm-debug.log* +web/yarn-debug.log* +web/yarn-error.log* + +# Go specific +proxy/vendor/ +*.test +*.prof +coverage.txt + +# Logs +logs/ +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..87234a4 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,85 @@ +# Multi-stage Dockerfile for Claude Code Proxy +# Builds both Go proxy server and Remix frontend in a single container + +# Stage 1: Build Go Backend +FROM golang:1.21-alpine AS go-builder + +WORKDIR /app + +# Install build dependencies +RUN apk add --no-cache git + +# Copy Go modules +COPY proxy/go.mod proxy/go.sum ./proxy/ +WORKDIR /app/proxy +RUN go mod download + +# Copy Go source code +COPY proxy/ ./ +RUN go build -o /app/bin/proxy cmd/proxy/main.go + +# Stage 2: Build Node.js Frontend +FROM node:20-alpine AS node-builder + +WORKDIR /app + +# Copy package files +COPY web/package*.json ./web/ +WORKDIR /app/web +RUN npm ci --only=production + +# Copy web source code and build +COPY web/ ./ +RUN npm run build + +# Stage 3: Production Runtime +FROM node:20-alpine + +WORKDIR /app + +# Install process manager for running multiple services +RUN npm install -g pm2 + +# Create app user for security +RUN addgroup -g 1001 -S appgroup && \ + adduser -S appuser -u 1001 -G appgroup + +# Copy built Go binary +COPY --from=go-builder /app/bin/proxy ./bin/proxy +RUN chmod +x ./bin/proxy + +# Copy built Remix application +COPY --from=node-builder /app/web/build ./web/build +COPY --from=node-builder /app/web/package*.json ./web/ +COPY --from=node-builder /app/web/node_modules ./web/node_modules + +# Create data directory for SQLite database +RUN mkdir -p /app/data && chown -R appuser:appgroup /app + +# Copy startup script +COPY docker-entrypoint.sh ./ +RUN chmod +x docker-entrypoint.sh + +# Environment variables with defaults +ENV PORT=3001 +ENV WEB_PORT=5173 +ENV READ_TIMEOUT=600 +ENV WRITE_TIMEOUT=600 +ENV IDLE_TIMEOUT=600 +ENV ANTHROPIC_FORWARD_URL=https://api.anthropic.com +ENV ANTHROPIC_VERSION=2023-06-01 +ENV ANTHROPIC_MAX_RETRIES=3 +ENV DB_PATH=/app/data/requests.db + +# Expose ports +EXPOSE 3001 5173 + +# Switch to app user +USER appuser + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \ + CMD wget --no-verbose --tries=1 --spider http://localhost:${PORT}/health || exit 1 + +# Start both services +CMD ["./docker-entrypoint.sh"] \ No newline at end of file diff --git a/README.md b/README.md index 010f300..225a29f 100644 --- a/README.md +++ b/README.md @@ -22,12 +22,14 @@ Claude Code Proxy serves two main purposes: ## Quick Start ### Prerequisites -- Go 1.20+ -- Node.js 18+ +- **Option 1**: Go 1.20+ and Node.js 18+ (for local development) +- **Option 2**: Docker (for containerized deployment) - Claude Code ### Installation +#### Option 1: Local Development + 1. **Clone the repository** ```bash git clone https://github.com/seifghazi/claude-code-proxy.git @@ -57,7 +59,59 @@ Claude Code Proxy serves two main purposes: ./run.sh ``` -5. **Using with Claude Code** +#### Option 2: Docker + +1. **Clone the repository** + ```bash + git clone https://github.com/seifghazi/claude-code-proxy.git + cd claude-code-proxy + ``` + +2. **Build and run with Docker** + ```bash + # Build the image + docker build -t claude-code-proxy . + + # Run with default settings + docker run -p 3001:3001 -p 5173:5173 claude-code-proxy + ``` + +3. **Run with persistent data and custom configuration** + ```bash + # Create a data directory for persistent SQLite database + mkdir -p ./data + + # Run with volume mount and custom environment variables + docker run -p 3001:3001 -p 5173:5173 \ + -v ./data:/app/data \ + -e ANTHROPIC_FORWARD_URL=https://api.anthropic.com \ + -e PORT=3001 \ + -e WEB_PORT=5173 \ + claude-code-proxy + ``` + +4. **Docker Compose (alternative)** + ```yaml + # docker-compose.yml + version: '3.8' + services: + claude-code-proxy: + build: . + ports: + - "3001:3001" + - "5173:5173" + volumes: + - ./data:/app/data + environment: + - ANTHROPIC_FORWARD_URL=https://api.anthropic.com + - PORT=3001 + - WEB_PORT=5173 + - DB_PATH=/app/data/requests.db + ``` + + Then run: `docker-compose up` + +### Using with Claude Code To use this proxy with Claude Code, set: ```bash @@ -100,6 +154,7 @@ make help # Show all commands ## Configuration +### Local Development Create a `.env` file with: ``` PORT=3001 @@ -109,6 +164,33 @@ ANTHROPIC_FORWARD_URL=https://api.anthropic.com See `.env.example` for all available options. +### Docker Environment Variables + +All environment variables can be configured when running the Docker container: + +| Variable | Default | Description | +|----------|---------|-------------| +| `PORT` | `3001` | Proxy server port | +| `WEB_PORT` | `5173` | Web dashboard port | +| `READ_TIMEOUT` | `600` | Server read timeout (seconds) | +| `WRITE_TIMEOUT` | `600` | Server write timeout (seconds) | +| `IDLE_TIMEOUT` | `600` | Server idle timeout (seconds) | +| `ANTHROPIC_FORWARD_URL` | `https://api.anthropic.com` | Target Anthropic API URL | +| `ANTHROPIC_VERSION` | `2023-06-01` | Anthropic API version | +| `ANTHROPIC_MAX_RETRIES` | `3` | Maximum retry attempts | +| `DB_PATH` | `/app/data/requests.db` | SQLite database path | + +Example with custom configuration: +```bash +docker run -p 3001:3001 -p 5173:5173 \ + -v ./data:/app/data \ + -e PORT=8080 \ + -e WEB_PORT=3000 \ + -e ANTHROPIC_FORWARD_URL=https://api.anthropic.com \ + -e DB_PATH=/app/data/custom.db \ + claude-code-proxy +``` + ## Project Structure diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh new file mode 100644 index 0000000..bbf82f8 --- /dev/null +++ b/docker-entrypoint.sh @@ -0,0 +1,122 @@ +#!/bin/sh + +# Docker entrypoint script for Claude Code Proxy +# Starts both the Go proxy server and Remix frontend + +set -e + +echo "🚀 Starting Claude Code Proxy services..." +echo "=========================================" + +# Function to handle graceful shutdown +cleanup() { + echo "" + echo "🛑 Shutting down services..." + pm2 delete all 2>/dev/null || true + exit 0 +} + +# Trap signals for graceful shutdown +trap cleanup SIGTERM SIGINT + +# Create PM2 ecosystem file +cat > /tmp/ecosystem.config.js << EOF +module.exports = { + apps: [ + { + name: 'proxy-server', + script: './bin/proxy', + cwd: '/app', + env: { + PORT: '${PORT}', + READ_TIMEOUT: '${READ_TIMEOUT}s', + WRITE_TIMEOUT: '${WRITE_TIMEOUT}s', + IDLE_TIMEOUT: '${IDLE_TIMEOUT}s', + ANTHROPIC_FORWARD_URL: '${ANTHROPIC_FORWARD_URL}', + ANTHROPIC_VERSION: '${ANTHROPIC_VERSION}', + ANTHROPIC_MAX_RETRIES: '${ANTHROPIC_MAX_RETRIES}', + DB_PATH: '${DB_PATH}' + }, + error_file: '/dev/stderr', + out_file: '/dev/stdout', + log_file: '/dev/stdout', + time: true + }, + { + name: 'web-server', + script: 'npm', + args: 'start', + cwd: '/app/web', + env: { + PORT: '${WEB_PORT}', + NODE_ENV: 'production' + }, + error_file: '/dev/stderr', + out_file: '/dev/stdout', + log_file: '/dev/stdout', + time: true + } + ] +}; +EOF + +echo "📊 Configuration:" +echo " - Proxy Server: http://localhost:${PORT}" +echo " - Web Dashboard: http://localhost:${WEB_PORT}" +echo " - Database: ${DB_PATH}" +echo " - Anthropic API: ${ANTHROPIC_FORWARD_URL}" +echo "=========================================" + +# Start services with PM2 +echo "🔄 Starting proxy server..." +pm2 start /tmp/ecosystem.config.js --only proxy-server --no-daemon & + +# Wait for proxy to be ready +echo "⏳ Waiting for proxy server to start..." +timeout=30 +while [ $timeout -gt 0 ]; do + if wget --quiet --spider "http://localhost:${PORT}/health" 2>/dev/null; then + echo "✅ Proxy server is ready" + break + fi + sleep 1 + timeout=$((timeout - 1)) +done + +if [ $timeout -eq 0 ]; then + echo "❌ Proxy server failed to start within 30 seconds" + exit 1 +fi + +echo "🔄 Starting web server..." +pm2 start /tmp/ecosystem.config.js --only web-server --no-daemon & + +# Wait for web server to be ready +echo "⏳ Waiting for web server to start..." +timeout=30 +while [ $timeout -gt 0 ]; do + if wget --quiet --spider "http://localhost:${WEB_PORT}" 2>/dev/null; then + echo "✅ Web server is ready" + break + fi + sleep 1 + timeout=$((timeout - 1)) +done + +if [ $timeout -eq 0 ]; then + echo "❌ Web server failed to start within 30 seconds" + exit 1 +fi + +echo "" +echo "✨ All services started successfully!" +echo "=========================================" +echo "📊 Web Dashboard: http://localhost:${WEB_PORT}" +echo "🔌 API Proxy: http://localhost:${PORT}" +echo "💚 Health Check: http://localhost:${PORT}/health" +echo "=========================================" +echo "💡 To use with Claude Code, set: ANTHROPIC_BASE_URL=http://localhost:${PORT}" +echo "" + +# Keep container running and show logs +pm2 logs --raw \ No newline at end of file From cb36631b7a0b75a82703b03c186268fe84615d61 Mon Sep 17 00:00:00 2001 From: Arseniy Ivanov Date: Sun, 3 Aug 2025 17:45:42 -0400 Subject: [PATCH 2/2] Fix Docker implementation with working SQLite and healthcheck MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Enable CGO in Go build stage for SQLite support (add gcc, musl-dev, sqlite-dev) - Replace PM2 with direct process management for simpler container operation - Fix network binding to 0.0.0.0 for external access from host - Simplify healthcheck command to avoid spider mode issues - Remove PM2 dependency and use background processes with proper signal handling - Both proxy (3001) and web (5173) services now start and respond correctly 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- Dockerfile | 18 +++++--- docker-entrypoint.sh | 105 ++++++++++--------------------------------- 2 files changed, 34 insertions(+), 89 deletions(-) diff --git a/Dockerfile b/Dockerfile index 87234a4..6891ded 100644 --- a/Dockerfile +++ b/Dockerfile @@ -6,8 +6,8 @@ FROM golang:1.21-alpine AS go-builder WORKDIR /app -# Install build dependencies -RUN apk add --no-cache git +# Install build dependencies including gcc for CGO +RUN apk add --no-cache git gcc musl-dev sqlite-dev # Copy Go modules COPY proxy/go.mod proxy/go.sum ./proxy/ @@ -16,7 +16,8 @@ RUN go mod download # Copy Go source code COPY proxy/ ./ -RUN go build -o /app/bin/proxy cmd/proxy/main.go +# Build with CGO enabled for SQLite support +RUN CGO_ENABLED=1 GOOS=linux go build -a -installsuffix cgo -o /app/bin/proxy cmd/proxy/main.go # Stage 2: Build Node.js Frontend FROM node:20-alpine AS node-builder @@ -26,19 +27,22 @@ WORKDIR /app # Copy package files COPY web/package*.json ./web/ WORKDIR /app/web -RUN npm ci --only=production +RUN npm ci # Copy web source code and build COPY web/ ./ RUN npm run build +# Clean up dev dependencies after build +RUN npm ci --only=production && npm cache clean --force + # Stage 3: Production Runtime FROM node:20-alpine WORKDIR /app -# Install process manager for running multiple services -RUN npm install -g pm2 +# Install runtime dependencies +RUN apk add --no-cache sqlite wget # Create app user for security RUN addgroup -g 1001 -S appgroup && \ @@ -79,7 +83,7 @@ USER appuser # Health check HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \ - CMD wget --no-verbose --tries=1 --spider http://localhost:${PORT}/health || exit 1 + CMD wget -qO- http://localhost:3001/health > /dev/null || exit 1 # Start both services CMD ["./docker-entrypoint.sh"] \ No newline at end of file diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh index bbf82f8..1ea9e68 100644 --- a/docker-entrypoint.sh +++ b/docker-entrypoint.sh @@ -12,101 +12,42 @@ echo "=========================================" cleanup() { echo "" echo "🛑 Shutting down services..." - pm2 delete all 2>/dev/null || true + kill $PROXY_PID $WEB_PID 2>/dev/null || true exit 0 } # Trap signals for graceful shutdown trap cleanup SIGTERM SIGINT -# Create PM2 ecosystem file -cat > /tmp/ecosystem.config.js << EOF -module.exports = { - apps: [ - { - name: 'proxy-server', - script: './bin/proxy', - cwd: '/app', - env: { - PORT: '${PORT}', - READ_TIMEOUT: '${READ_TIMEOUT}s', - WRITE_TIMEOUT: '${WRITE_TIMEOUT}s', - IDLE_TIMEOUT: '${IDLE_TIMEOUT}s', - ANTHROPIC_FORWARD_URL: '${ANTHROPIC_FORWARD_URL}', - ANTHROPIC_VERSION: '${ANTHROPIC_VERSION}', - ANTHROPIC_MAX_RETRIES: '${ANTHROPIC_MAX_RETRIES}', - DB_PATH: '${DB_PATH}' - }, - error_file: '/dev/stderr', - out_file: '/dev/stdout', - log_file: '/dev/stdout', - time: true - }, - { - name: 'web-server', - script: 'npm', - args: 'start', - cwd: '/app/web', - env: { - PORT: '${WEB_PORT}', - NODE_ENV: 'production' - }, - error_file: '/dev/stderr', - out_file: '/dev/stdout', - log_file: '/dev/stdout', - time: true - } - ] -}; -EOF - echo "📊 Configuration:" -echo " - Proxy Server: http://localhost:${PORT}" -echo " - Web Dashboard: http://localhost:${WEB_PORT}" +echo " - Proxy Server: http://0.0.0.0:${PORT}" +echo " - Web Dashboard: http://0.0.0.0:${WEB_PORT}" echo " - Database: ${DB_PATH}" echo " - Anthropic API: ${ANTHROPIC_FORWARD_URL}" echo "=========================================" -# Start services with PM2 +# Start proxy server echo "🔄 Starting proxy server..." -pm2 start /tmp/ecosystem.config.js --only proxy-server --no-daemon & +PORT=${PORT} \ +READ_TIMEOUT=${READ_TIMEOUT}s \ +WRITE_TIMEOUT=${WRITE_TIMEOUT}s \ +IDLE_TIMEOUT=${IDLE_TIMEOUT}s \ +ANTHROPIC_FORWARD_URL=${ANTHROPIC_FORWARD_URL} \ +ANTHROPIC_VERSION=${ANTHROPIC_VERSION} \ +ANTHROPIC_MAX_RETRIES=${ANTHROPIC_MAX_RETRIES} \ +DB_PATH=${DB_PATH} \ +./bin/proxy & +PROXY_PID=$! -# Wait for proxy to be ready -echo "⏳ Waiting for proxy server to start..." -timeout=30 -while [ $timeout -gt 0 ]; do - if wget --quiet --spider "http://localhost:${PORT}/health" 2>/dev/null; then - echo "✅ Proxy server is ready" - break - fi - sleep 1 - timeout=$((timeout - 1)) -done - -if [ $timeout -eq 0 ]; then - echo "❌ Proxy server failed to start within 30 seconds" - exit 1 -fi +# Wait for proxy to start +sleep 3 +# Start web server echo "🔄 Starting web server..." -pm2 start /tmp/ecosystem.config.js --only web-server --no-daemon & - -# Wait for web server to be ready -echo "⏳ Waiting for web server to start..." -timeout=30 -while [ $timeout -gt 0 ]; do - if wget --quiet --spider "http://localhost:${WEB_PORT}" 2>/dev/null; then - echo "✅ Web server is ready" - break - fi - sleep 1 - timeout=$((timeout - 1)) -done - -if [ $timeout -eq 0 ]; then - echo "❌ Web server failed to start within 30 seconds" - exit 1 -fi +cd web +PORT=${WEB_PORT} HOST=0.0.0.0 NODE_ENV=production npx remix-serve build/server/index.js & +WEB_PID=$! +cd .. echo "" echo "✨ All services started successfully!" @@ -118,5 +59,5 @@ echo "=========================================" echo "💡 To use with Claude Code, set: ANTHROPIC_BASE_URL=http://localhost:${PORT}" echo "" -# Keep container running and show logs -pm2 logs --raw \ No newline at end of file +# Wait for processes to finish +wait \ No newline at end of file