docker image to alpine, reduce size, compile scripts, entrypoint for alpine
This commit is contained in:
+19
-8
@@ -14,8 +14,8 @@ RUN \
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# Rebuild the source code only when needed
|
# Rebuild the source code only when needed
|
||||||
FROM base AS builder
|
FROM node:22-alpine AS builder
|
||||||
RUN apt-get update && apt-get install -y python3 make g++ && rm -rf /var/lib/apt/lists/*
|
RUN apk add --no-cache python3 make g++ curl
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
COPY --from=deps /app/node_modules ./node_modules
|
COPY --from=deps /app/node_modules ./node_modules
|
||||||
COPY . .
|
COPY . .
|
||||||
@@ -23,12 +23,21 @@ COPY . .
|
|||||||
# Rebuild better-sqlite3 for Alpine Linux
|
# Rebuild better-sqlite3 for Alpine Linux
|
||||||
RUN npm rebuild better-sqlite3
|
RUN npm rebuild better-sqlite3
|
||||||
|
|
||||||
|
# Build TypeScript database scripts to JavaScript
|
||||||
|
RUN node build-scripts.js
|
||||||
|
|
||||||
# Build the application
|
# Build the application
|
||||||
RUN npm run build
|
RUN npm run build
|
||||||
|
|
||||||
|
# Remove development-only dependencies and dedupe after build
|
||||||
|
RUN npm prune --omit=dev && npm dedupe --prod \
|
||||||
|
&& find node_modules -type f \( -name "README" -o -name "README.*" -o -name "CHANGELOG*" -o -name "*.md" -o -name "*.map" \) -delete \
|
||||||
|
&& find node_modules -type d \( -name "__tests__" -o -name "test" -o -name "tests" -o -name "docs" -o -name "examples" \) -prune -exec rm -rf {} + \
|
||||||
|
&& npm cache clean --force
|
||||||
|
|
||||||
# Production image, copy all the files and run next
|
# Production image, copy all the files and run next
|
||||||
FROM base AS runner
|
FROM node:22-alpine AS runner
|
||||||
RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*
|
RUN apk add --no-cache curl
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
ENV NODE_ENV=production
|
ENV NODE_ENV=production
|
||||||
@@ -38,17 +47,19 @@ ENV NEXT_TELEMETRY_DISABLED=1
|
|||||||
COPY --from=builder /app/.next/standalone ./
|
COPY --from=builder /app/.next/standalone ./
|
||||||
COPY --from=builder --chown=root:root /app/.next/static ./.next/static
|
COPY --from=builder --chown=root:root /app/.next/static ./.next/static
|
||||||
|
|
||||||
# Copy full node_modules for database operations
|
# Copy compiled database scripts instead of TypeScript sources
|
||||||
|
COPY --from=builder /app/dist ./dist
|
||||||
|
|
||||||
|
# Copy minimal runtime dependencies for database operations
|
||||||
COPY --from=builder /app/node_modules ./node_modules
|
COPY --from=builder /app/node_modules ./node_modules
|
||||||
|
|
||||||
# Copy database setup scripts and package.json
|
# Copy database config and package.json
|
||||||
COPY --from=builder /app/scripts ./scripts
|
|
||||||
COPY --from=builder /app/lib ./lib
|
COPY --from=builder /app/lib ./lib
|
||||||
COPY --from=builder /app/drizzle.config.ts ./drizzle.config.ts
|
COPY --from=builder /app/drizzle.config.ts ./drizzle.config.ts
|
||||||
COPY --from=builder /app/package.json ./package.json
|
COPY --from=builder /app/package.json ./package.json
|
||||||
|
|
||||||
# Copy entrypoint script
|
# Copy entrypoint script
|
||||||
COPY docker-entrypoint.sh /usr/local/bin/
|
COPY docker-entrypoint-alpine.sh /usr/local/bin/docker-entrypoint.sh
|
||||||
RUN chmod +x /usr/local/bin/docker-entrypoint.sh
|
RUN chmod +x /usr/local/bin/docker-entrypoint.sh
|
||||||
|
|
||||||
# Create public directory if it doesn't exist
|
# Create public directory if it doesn't exist
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
const { execSync } = require('child_process');
|
||||||
|
|
||||||
|
console.log('📦 Building TypeScript database scripts...');
|
||||||
|
|
||||||
|
// Create dist directory
|
||||||
|
const distDir = path.join(__dirname, 'dist');
|
||||||
|
if (!fs.existsSync(distDir)) {
|
||||||
|
fs.mkdirSync(distDir, { recursive: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bundle scripts with esbuild (available via Next.js)
|
||||||
|
const scripts = [
|
||||||
|
'scripts/check-database.ts',
|
||||||
|
'scripts/setup-database.ts'
|
||||||
|
];
|
||||||
|
|
||||||
|
scripts.forEach(script => {
|
||||||
|
const scriptName = path.basename(script, '.ts');
|
||||||
|
const outFile = path.join(distDir, `${scriptName}.js`);
|
||||||
|
|
||||||
|
console.log(` Building ${script} -> ${outFile}`);
|
||||||
|
|
||||||
|
try {
|
||||||
|
execSync(`npx esbuild ${script} --bundle --platform=node --target=node22 --outfile=${outFile} --external:better-sqlite3`, {
|
||||||
|
stdio: 'pipe'
|
||||||
|
});
|
||||||
|
console.log(` ✅ ${scriptName}.js built successfully`);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(` ❌ Failed to build ${scriptName}:`, error.message);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('🎉 All database scripts built successfully!');
|
||||||
@@ -14,14 +14,6 @@ if [ ! -f .env.production ]; then
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Create necessary directories
|
|
||||||
echo "📁 Creating necessary directories..."
|
|
||||||
mkdir -p data backups logs
|
|
||||||
|
|
||||||
# Set proper permissions
|
|
||||||
echo "🔒 Setting directory permissions..."
|
|
||||||
chmod 755 data backups logs
|
|
||||||
|
|
||||||
# Pull latest changes (if using git)
|
# Pull latest changes (if using git)
|
||||||
if [ -d ".git" ]; then
|
if [ -d ".git" ]; then
|
||||||
echo "📦 Pulling latest changes..."
|
echo "📦 Pulling latest changes..."
|
||||||
@@ -29,59 +21,57 @@ if [ -d ".git" ]; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# Build and deploy with Docker Compose
|
# Build and deploy with Docker Compose
|
||||||
echo "� Building and starting Docker containers..."
|
echo "🐳 Building and deploying containers..."
|
||||||
echo "🐳 Building and starting Docker containers..."
|
|
||||||
|
|
||||||
# Stop existing containers
|
# Stop existing containers
|
||||||
docker compose -f docker-compose.production.yml down || echo "No existing containers to stop"
|
docker compose -f docker-compose.production.yml down || echo "No existing containers to stop"
|
||||||
|
|
||||||
# Build and start containers
|
# Build and start containers (database initialization is automated)
|
||||||
docker compose -f docker-compose.production.yml up -d --build
|
docker compose -f docker-compose.production.yml up -d --build
|
||||||
|
|
||||||
# Wait for containers to be healthy
|
# Wait for health check to pass (container has built-in health checks)
|
||||||
echo "⏳ Waiting for containers to be healthy..."
|
echo "⏳ Waiting for application to be ready..."
|
||||||
sleep 30
|
timeout=60
|
||||||
|
counter=0
|
||||||
# Check health
|
while [ $counter -lt $timeout ]; do
|
||||||
echo "🔍 Checking application health..."
|
if docker compose -f docker-compose.production.yml ps tt-booking | grep -q "healthy"; then
|
||||||
for i in {1..10}; do
|
|
||||||
if curl -f http://localhost:3036/api/health >/dev/null 2>&1; then
|
|
||||||
echo "✅ Application is healthy!"
|
echo "✅ Application is healthy!"
|
||||||
break
|
break
|
||||||
elif [ $i -eq 10 ]; then
|
elif [ $counter -eq $((timeout-10)) ]; then
|
||||||
echo "❌ Application health check failed after 10 attempts"
|
echo "❌ Application failed to become healthy within ${timeout}s"
|
||||||
echo "📋 Container logs:"
|
echo "📋 Container logs:"
|
||||||
docker compose -f docker-compose.production.yml logs tt-booking
|
docker compose -f docker-compose.production.yml logs --tail=30 tt-booking
|
||||||
exit 1
|
exit 1
|
||||||
else
|
else
|
||||||
echo "⏳ Attempt $i/10: Application not ready yet, waiting..."
|
echo "⏳ Waiting for health check... (${counter}s/${timeout}s)"
|
||||||
sleep 10
|
sleep 5
|
||||||
|
counter=$((counter+5))
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
# Show running containers
|
# Show deployment status
|
||||||
echo "📊 Running containers:"
|
echo "📊 Deployment Status:"
|
||||||
docker compose -f docker-compose.production.yml ps
|
docker compose -f docker-compose.production.yml ps tt-booking
|
||||||
|
|
||||||
# Show logs
|
|
||||||
echo "📋 Recent application logs:"
|
echo "📋 Recent application logs:"
|
||||||
docker compose -f docker-compose.production.yml logs --tail=20 tt-booking
|
docker compose -f docker-compose.production.yml logs --tail=10 tt-booking
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo "🎉 Deployment completed successfully!"
|
echo "🎉 Deployment completed successfully!"
|
||||||
echo ""
|
echo ""
|
||||||
echo "📊 Application Status:"
|
echo "📊 Application Details:"
|
||||||
echo " • URL: https://lcc-tt-booking.mikicvi.com"
|
echo " - URL: https://lcc-tt-booking.mikicvi.com"
|
||||||
echo " • Health Check: http://localhost:3036/api/health"
|
echo " - Local: http://localhost:3036"
|
||||||
echo " • Container Status: $(docker compose -f docker-compose.production.yml ps -q tt-booking | xargs docker inspect -f '{{.State.Status}}')"
|
echo " - Health: http://localhost:3036/api/health"
|
||||||
echo ""
|
echo ""
|
||||||
echo "🔧 Useful commands:"
|
echo "🔧 Management commands:"
|
||||||
echo " • View logs: docker compose -f docker-compose.production.yml logs -f tt-booking"
|
echo " - Logs: docker compose -f docker-compose.production.yml logs -f tt-booking"
|
||||||
echo " • Restart: docker compose -f docker-compose.production.yml restart tt-booking"
|
echo " - Restart: docker compose -f docker-compose.production.yml restart tt-booking"
|
||||||
echo " • Stop: docker compose -f docker-compose.production.yml down"
|
echo " - Stop: docker compose -f docker-compose.production.yml down"
|
||||||
|
echo " - Shell: docker compose -f docker-compose.production.yml exec tt-booking sh"
|
||||||
echo ""
|
echo ""
|
||||||
echo "⚠️ Don't forget to:"
|
echo "⚠️ Post-deployment checklist:"
|
||||||
echo " 1. Set up Cloudflare Tunnel to expose your application"
|
echo " - Cloudflare Tunnel is configured and running"
|
||||||
echo " 2. Update your .env.production with real email credentials"
|
echo " - Admin password changed from default"
|
||||||
echo " 3. Change the default admin password"
|
echo " - Email settings configured in .env.production"
|
||||||
echo ""
|
echo ""
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
set -e
|
||||||
|
|
||||||
|
echo "🚀 Starting TT Booking Production..."
|
||||||
|
|
||||||
|
# Create required directories if they don't exist
|
||||||
|
echo "📁 Ensuring directories exist..."
|
||||||
|
mkdir -p /app/data /app/backups /app/logs
|
||||||
|
chmod 755 /app/data /app/backups /app/logs
|
||||||
|
|
||||||
|
# Set defaults for environment variables
|
||||||
|
export DATABASE_URL="${DATABASE_URL:-/app/data/sqlite.db}"
|
||||||
|
export NODE_ENV="${NODE_ENV:-production}"
|
||||||
|
export ADMIN_EMAIL="${ADMIN_EMAIL:-admin@tabletennis.com}"
|
||||||
|
export ADMIN_PASSWORD="${ADMIN_PASSWORD:-admin123}"
|
||||||
|
|
||||||
|
# Validate required environment variables
|
||||||
|
if [ -z "$NEXTAUTH_SECRET" ]; then
|
||||||
|
echo "❌ NEXTAUTH_SECRET is required but not set"
|
||||||
|
echo "💡 Generate one with: openssl rand -base64 32"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$NEXTAUTH_URL" ]; then
|
||||||
|
echo "❌ NEXTAUTH_URL is required but not set"
|
||||||
|
echo "💡 Set to your application URL, e.g., https://your-domain.com"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check database state and determine what actions are needed
|
||||||
|
echo "📊 Analyzing database state..."
|
||||||
|
DB_CHECK_EXIT=0
|
||||||
|
node dist/check-database.js || DB_CHECK_EXIT=$?
|
||||||
|
|
||||||
|
case $DB_CHECK_EXIT in
|
||||||
|
0)
|
||||||
|
echo "✅ Database is ready - no action needed"
|
||||||
|
;;
|
||||||
|
1)
|
||||||
|
echo "🔧 Database needs migration..."
|
||||||
|
npm run db:push
|
||||||
|
echo "✅ Migration completed"
|
||||||
|
;;
|
||||||
|
2)
|
||||||
|
echo "🌱 Database needs seeding..."
|
||||||
|
echo " Admin Email: $ADMIN_EMAIL"
|
||||||
|
echo " Admin Password: [HIDDEN]"
|
||||||
|
node dist/setup-database.js --essential-only
|
||||||
|
echo "✅ Seeding completed"
|
||||||
|
echo "💡 You can now login with: $ADMIN_EMAIL / $ADMIN_PASSWORD"
|
||||||
|
;;
|
||||||
|
3)
|
||||||
|
echo "🔄 Database needs migration and seeding..."
|
||||||
|
npm run db:push
|
||||||
|
echo "✅ Migration completed"
|
||||||
|
echo "🌱 Seeding database..."
|
||||||
|
echo " Admin Email: $ADMIN_EMAIL"
|
||||||
|
echo " Admin Password: [HIDDEN]"
|
||||||
|
node dist/setup-database.js --essential-only
|
||||||
|
echo "✅ Database initialization completed"
|
||||||
|
echo "💡 You can now login with: $ADMIN_EMAIL / $ADMIN_PASSWORD"
|
||||||
|
;;
|
||||||
|
4)
|
||||||
|
echo "❌ Database state check failed - see logs above"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "❌ Unexpected database check result: $DB_CHECK_EXIT"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
echo "🌟 Starting server..."
|
||||||
|
|
||||||
|
# Execute the main command
|
||||||
|
exec "$@"
|
||||||
Reference in New Issue
Block a user