From dfebb51bd2e1262442d17c45baf3ef8f8f3c9e4f Mon Sep 17 00:00:00 2001 From: root Date: Mon, 9 Feb 2026 20:17:26 +0500 Subject: [PATCH] Refine Docker, docker-compose, and EC2 deploy Co-authored-by: Cursor --- .github/workflows/deploy-ec2.yml | 50 +++++++++++++++++++++++--------- Dockerfile | 11 +++++-- docker-compose.yml | 23 +++++++++++---- src/app/api/health/route.ts | 26 +++++++++++++++++ 4 files changed, 89 insertions(+), 21 deletions(-) create mode 100644 src/app/api/health/route.ts diff --git a/.github/workflows/deploy-ec2.yml b/.github/workflows/deploy-ec2.yml index 741fc74..fc82155 100644 --- a/.github/workflows/deploy-ec2.yml +++ b/.github/workflows/deploy-ec2.yml @@ -37,6 +37,8 @@ jobs: - name: Deploy to EC2 with rollback uses: appleboy/ssh-action@v1.2.0 + env: + EC2_ENV: ${{ secrets.EC2_ENV }} with: host: ${{ secrets.EC2_HOST }} username: ${{ secrets.EC2_USER }} @@ -51,11 +53,7 @@ jobs: mkdir -p "$APP_DIR" cd "$APP_DIR" - echo "Writing .env file from EC2_ENV secret..." - # Overwrite .env on the server with the contents of the EC2_ENV variable - # EC2_ENV is provided by GitHub as a multi-line secret matching your local .env - printf '%s\n' "$EC2_ENV" > .env - + # Initialize git repo if needed if [ ! -d ".git" ]; then echo "Cloning repository for the first time..." git clone ${{ github.repositoryUrl }} . @@ -71,15 +69,33 @@ jobs: git checkout ${{ github.ref_name }} git reset --hard ${{ github.sha }} - echo "Pulling docker images and rebuilding containers..." - if command -v docker-compose >/dev/null 2>&1; then - DC="docker-compose" - else - DC="docker compose" + echo "Writing .env file from EC2_ENV secret..." + # Write .env file AFTER pulling latest code so it's in the right location + # EC2_ENV is provided by GitHub as a multi-line secret matching your local .env + printf '%s\n' "$EC2_ENV" > .env + + echo "Checking Docker and Docker Compose..." + if ! command -v docker >/dev/null 2>&1; then + echo "Error: Docker is not installed" + exit 1 fi - $DC pull || true + if command -v docker-compose >/dev/null 2>&1; then + DC="docker-compose" + elif docker compose version >/dev/null 2>&1; then + DC="docker compose" + else + echo "Error: Docker Compose is not installed" + exit 1 + fi + echo "Using: $DC" + $DC version + + echo "Stopping existing containers..." + $DC down || true + + echo "Building and starting containers with docker-compose..." set +e $DC up -d --build DEPLOY_EXIT=$? @@ -89,26 +105,32 @@ jobs: echo "Deployment failed, attempting rollback..." if [ -n "$PREV_COMMIT" ]; then git reset --hard "$PREV_COMMIT" + printf '%s\n' "$EC2_ENV" > .env $DC up -d --build || true fi exit 1 fi + echo "Waiting for containers to be healthy..." + sleep 10 + echo "Running health check..." HEALTH_URL="${HEALTH_URL:-http://localhost:3000/api/health}" - for i in {1..10}; do + for i in {1..15}; do if curl -fsS "$HEALTH_URL" >/dev/null 2>&1; then - echo "Health check passed." + echo "Health check passed on attempt $i." + echo "Deployment successful!" exit 0 fi - echo "Health check attempt $i failed, retrying..." + echo "Health check attempt $i failed, retrying in 5 seconds..." sleep 5 done echo "Health check failed after multiple attempts, rolling back..." if [ -n "$PREV_COMMIT" ]; then git reset --hard "$PREV_COMMIT" + printf '%s\n' "$EC2_ENV" > .env $DC up -d --build || true fi exit 1 diff --git a/Dockerfile b/Dockerfile index 15554c5..ffe29e8 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,13 +2,20 @@ FROM node:20-alpine WORKDIR /app +# Copy package files COPY package*.json prisma.config.ts ./ COPY prisma ./prisma -RUN npm install && npx prisma generate +# Install dependencies and generate Prisma client +RUN npm install --only=production && npx prisma generate +# Copy application code COPY . . +# Build the Next.js application +RUN npm run build + EXPOSE 3000 -CMD ["npm", "run", "dev"] +# Use production start command +CMD ["npm", "start"] diff --git a/docker-compose.yml b/docker-compose.yml index c56ca17..6046db8 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -11,21 +11,34 @@ services: - "5444:5432" volumes: - postgres_data:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-postgres}"] + interval: 10s + timeout: 5s + retries: 5 app: build: context: . dockerfile: Dockerfile + restart: always ports: - "3000:3000" + env_file: + - .env environment: + # Override DATABASE_URL to use internal docker network hostname DATABASE_URL: postgresql://${POSTGRES_USER:-postgres}:${POSTGRES_PASSWORD:-password}@db:5432/${POSTGRES_DB:-dropshipping}?schema=public - NEXTAUTH_SECRET: ${NEXTAUTH_SECRET} - NEXTAUTH_URL: ${NEXTAUTH_URL:-http://localhost:3000} - GOOGLE_CLIENT_ID: ${GOOGLE_CLIENT_ID} - GOOGLE_CLIENT_SECRET: ${GOOGLE_CLIENT_SECRET} + NODE_ENV: production depends_on: - - db + db: + condition: service_healthy + healthcheck: + test: ["CMD", "node", "-e", "require('http').get('http://localhost:3000/api/health', (r) => { process.exit(r.statusCode === 200 ? 0 : 1) }).on('error', () => process.exit(1))"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 40s volumes: postgres_data: diff --git a/src/app/api/health/route.ts b/src/app/api/health/route.ts new file mode 100644 index 0000000..d4c4187 --- /dev/null +++ b/src/app/api/health/route.ts @@ -0,0 +1,26 @@ +import { NextResponse } from "next/server" +import { prisma } from "@/lib/prisma" + +export async function GET() { + try { + // Simple database connectivity check + await prisma.$queryRaw`SELECT 1` + + return NextResponse.json( + { + status: "ok", + timestamp: new Date().toISOString() + }, + { status: 200 } + ) + } catch (error) { + console.error("Health check failed:", error) + return NextResponse.json( + { + status: "error", + message: "Database connection failed" + }, + { status: 503 } + ) + } +}