diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..fd04bd9 --- /dev/null +++ b/.env.example @@ -0,0 +1,18 @@ +# MongoDB Configuration +MONGODB_URI=mongodb://mongo:27017/chatapp + +# JWT Configuration +JWT_SECRET=change-this-to-a-secure-random-string +JWT_EXPIRATION=3600 + +# Cookie Configuration +COOKIE_EXPIRATION=3600000 + +# AWS S3 Configuration (for file uploads) +AWS_ACCESS_KEY_ID=your-aws-access-key-id +AWS_SECRET_ACCESS_KEY=your-aws-secret-access-key +S3_BUCKET_NAME=your-s3-bucket-name +AWS_REGION=us-east-1 + +# Application Stage +STAGE=prod diff --git a/.github/workflows/docker-build-push.yml b/.github/workflows/docker-build-push.yml new file mode 100644 index 0000000..25af346 --- /dev/null +++ b/.github/workflows/docker-build-push.yml @@ -0,0 +1,114 @@ +name: Build and Push Docker Images + +on: + push: + branches: + - main + - develop + tags: + - 'v*.*.*' + pull_request: + branches: + - main + - develop + workflow_dispatch: + +env: + REGISTRY: ghcr.io + IMAGE_NAME_BACKEND: ${{ github.repository }}/backend + IMAGE_NAME_FRONTEND: ${{ github.repository }}/frontend + +jobs: + build-and-push-backend: + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Log in to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract metadata for Docker (backend) + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME_BACKEND }} + tags: | + type=ref,event=branch + type=ref,event=pr + type=semver,pattern={{version}} + type=semver,pattern={{major}}.{{minor}} + type=semver,pattern={{major}} + type=sha,prefix={{branch}}- + type=raw,value=latest,enable={{is_default_branch}} + + - name: Build and push Backend Docker image + uses: docker/build-push-action@v5 + with: + context: ./backend + file: ./backend/Dockerfile + push: ${{ github.event_name != 'pull_request' }} + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max + # Build for multiple platforms only on version tags for faster CI + platforms: ${{ startsWith(github.ref, 'refs/tags/v') && 'linux/amd64,linux/arm64' || 'linux/amd64' }} + + build-and-push-frontend: + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Log in to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract metadata for Docker (frontend) + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME_FRONTEND }} + tags: | + type=ref,event=branch + type=ref,event=pr + type=semver,pattern={{version}} + type=semver,pattern={{major}}.{{minor}} + type=semver,pattern={{major}} + type=sha,prefix={{branch}}- + type=raw,value=latest,enable={{is_default_branch}} + + - name: Build and push Frontend Docker image + uses: docker/build-push-action@v5 + with: + context: ./frontend + file: ./frontend/Dockerfile + push: ${{ github.event_name != 'pull_request' }} + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max + # Build for multiple platforms only on version tags for faster CI + platforms: ${{ startsWith(github.ref, 'refs/tags/v') && 'linux/amd64,linux/arm64' || 'linux/amd64' }} diff --git a/DOCKER.md b/DOCKER.md new file mode 100644 index 0000000..523195c --- /dev/null +++ b/DOCKER.md @@ -0,0 +1,210 @@ +# Docker Deployment Guide + +This guide explains how to build and deploy the Chat App using Docker and the automated GitHub Actions workflow. + +## Overview + +The Chat App consists of two main components: +- **Backend**: NestJS application (port 3000) +- **Frontend**: React application served by nginx (port 80) + +Each component has its own Dockerfile for building optimized production images. + +## GitHub Actions Workflow + +The repository includes an automated workflow (`.github/workflows/docker-build-push.yml`) that: + +1. **Builds Docker images** for both backend and frontend +2. **Pushes images** to GitHub Container Registry (ghcr.io) +3. **Tags images** appropriately based on the trigger: + - `latest` tag for the default branch (main) + - Branch name tags for branch pushes + - Semantic version tags for version tags (v*.*.*) + - PR tags for pull requests + - SHA-based tags for all builds + +### Triggering the Workflow + +The workflow automatically runs on: +- Push to `main` or `develop` branches +- Creation of version tags (e.g., `v1.0.0`) +- Pull requests to `main` or `develop` +- Manual trigger via GitHub Actions UI + +### Accessing Built Images + +After the workflow completes, images are available at: +- Backend: `ghcr.io/codingf0x/chat-app/backend:latest` +- Frontend: `ghcr.io/codingf0x/chat-app/frontend:latest` + +To pull and run the images: + +```bash +# Pull images +docker pull ghcr.io/codingf0x/chat-app/backend:latest +docker pull ghcr.io/codingf0x/chat-app/frontend:latest + +# Run backend +docker run -p 3000:3000 \ + -e MONGODB_URI=mongodb://your-mongo-uri \ + -e JWT_SECRET=your-secret \ + ghcr.io/codingf0x/chat-app/backend:latest + +# Run frontend +docker run -p 80:80 ghcr.io/codingf0x/chat-app/frontend:latest +``` + +## Local Docker Build + +### Building Images Locally + +To build the Docker images locally: + +```bash +# Build backend image +cd backend +docker build -t chat-app-backend . + +# Build frontend image +cd frontend +docker build -t chat-app-frontend . +``` + +### Running Locally with Docker + +```bash +# Run backend +docker run -d \ + --name chat-backend \ + -p 3000:3000 \ + -e MONGODB_URI=mongodb://localhost:27017/chatapp \ + -e JWT_SECRET=your-secret-key \ + -e AWS_ACCESS_KEY_ID=your-aws-key \ + -e AWS_SECRET_ACCESS_KEY=your-aws-secret \ + -e S3_BUCKET_NAME=your-bucket \ + chat-app-backend + +# Run frontend +docker run -d \ + --name chat-frontend \ + -p 80:80 \ + chat-app-frontend +``` + +## Docker Compose + +For easier local development and testing, use the included `docker-compose.yml`: + +```bash +# Copy environment variables template +cp .env.example .env + +# Edit .env with your configuration (REQUIRED: Set JWT_SECRET) +nano .env # or use your preferred editor + +# IMPORTANT: You MUST set JWT_SECRET in .env before starting +# Generate a secure secret: openssl rand -base64 32 + +# Start all services (backend, frontend, and MongoDB) +docker-compose up -d + +# View logs +docker-compose logs -f + +# Stop all services +docker-compose down + +# Stop and remove volumes (WARNING: This deletes database data) +docker-compose down -v +``` + +The docker-compose setup includes: +- **MongoDB**: Database service with persistent volume +- **Backend**: NestJS application connected to MongoDB +- **Frontend**: React application served by nginx +- **Health checks**: All services have health checks configured +- **Automatic restart**: Services restart on failure + +Access the application: +- Frontend: http://localhost +- Backend API: http://localhost:3000 +- MongoDB: localhost:27017 + +## Image Features + +### Backend Image +- **Base**: node:20-slim (optimized for size) +- **Multi-stage build**: Separate build and production stages +- **Security**: Runs as non-root user (nestjs:nodejs) +- **Health check**: Built-in health check endpoint +- **Port**: 3000 + +### Frontend Image +- **Base Builder**: node:20-slim +- **Base Production**: nginx:alpine (minimal size) +- **Multi-stage build**: Build with Node, serve with nginx +- **SPA Support**: Configured for React Router +- **Health check**: nginx health check +- **Port**: 80 + +## Environment Variables + +### Backend Required Variables: +- `MONGODB_URI`: MongoDB connection string +- `JWT_SECRET`: Secret for JWT token generation +- `AWS_ACCESS_KEY_ID`: AWS credentials for S3 +- `AWS_SECRET_ACCESS_KEY`: AWS credentials for S3 +- `S3_BUCKET_NAME`: S3 bucket for file uploads +- `STAGE`: Environment stage (dev/prod) + +### Frontend Configuration: +The frontend build includes the API endpoint configuration. Update the environment variables before building if needed. + +## Troubleshooting + +### Build Issues +- **Circular dependency**: The Dockerfiles automatically remove the circular `"backend": "file:"` and `"frontend": "file:"` dependencies from package.json +- **Network timeouts**: Use `--legacy-peer-deps` flag (already included in Dockerfiles) +- **Missing packages**: The build uses `npm install` without lock file to avoid corruption issues + +### Runtime Issues +- **Backend won't start**: Check environment variables, especially `MONGODB_URI` +- **Frontend 404 errors**: nginx is configured for SPA routing, check browser console for API errors +- **Connection refused**: Ensure backend is accessible at the configured API endpoint + +## Production Deployment + +For production deployment: + +1. **Use version tags** instead of `latest`: + ```bash + docker pull ghcr.io/codingf0x/chat-app/backend:v1.0.0 + ``` + +2. **Set proper environment variables** using secrets management + +3. **Use orchestration** (Kubernetes, Docker Swarm, or ECS) for: + - Auto-scaling + - Load balancing + - Health checks + - Rolling updates + +4. **Monitor logs**: + ```bash + docker logs -f chat-backend + docker logs -f chat-frontend + ``` + +## GitHub Container Registry Authentication + +To push/pull from GHCR locally: + +```bash +# Login to GHCR +echo $GITHUB_TOKEN | docker login ghcr.io -u USERNAME --password-stdin + +# Pull private images +docker pull ghcr.io/codingf0x/chat-app/backend:latest +``` + +The GitHub Actions workflow uses `GITHUB_TOKEN` automatically, no manual configuration needed. diff --git a/QUICKSTART.md b/QUICKSTART.md new file mode 100644 index 0000000..928f104 --- /dev/null +++ b/QUICKSTART.md @@ -0,0 +1,173 @@ +# Quick Start Guide - Docker Deployment + +This guide will help you quickly set up and run the Chat App using Docker. + +## Prerequisites + +- Docker installed (version 20.10 or higher) +- Docker Compose installed (version 2.0 or higher) +- Git (to clone the repository) + +## Quick Start (Local Development) + +1. **Clone the repository:** + ```bash + git clone https://github.com/CodingF0X/Chat-app.git + cd Chat-app + ``` + +2. **Configure environment variables:** + ```bash + cp .env.example .env + # Edit .env with your actual AWS credentials and secrets + nano .env + ``` + +3. **Start all services:** + ```bash + docker-compose up -d + ``` + +4. **Access the application:** + - Frontend: http://localhost + - Backend API: http://localhost:3000 + - MongoDB: localhost:27017 + +5. **View logs:** + ```bash + # All services + docker-compose logs -f + + # Specific service + docker-compose logs -f backend + docker-compose logs -f frontend + ``` + +6. **Stop services:** + ```bash + docker-compose down + ``` + +## Quick Start (Using Pre-built Images from GitHub Container Registry) + +If the Docker images have been built and published via GitHub Actions: + +1. **Pull the images:** + ```bash + docker pull ghcr.io/codingf0x/chat-app/backend:latest + docker pull ghcr.io/codingf0x/chat-app/frontend:latest + ``` + +2. **Run MongoDB:** + ```bash + docker run -d \ + --name chat-mongo \ + -p 27017:27017 \ + -v mongo-data:/data/db \ + mongo:latest + ``` + +3. **Run Backend:** + ```bash + docker run -d \ + --name chat-backend \ + -p 3000:3000 \ + -e MONGODB_URI=mongodb://host.docker.internal:27017/chatapp \ + -e JWT_SECRET=your-secret-key \ + -e AWS_ACCESS_KEY_ID=your-aws-key \ + -e AWS_SECRET_ACCESS_KEY=your-aws-secret \ + -e S3_BUCKET_NAME=your-bucket \ + ghcr.io/codingf0x/chat-app/backend:latest + ``` + + **Note for Linux users:** Replace `host.docker.internal` with `172.17.0.1` (Docker bridge IP) or use `--network host` mode. + + ```bash + # Alternative for Linux + docker run -d \ + --name chat-backend \ + -p 3000:3000 \ + -e MONGODB_URI=mongodb://172.17.0.1:27017/chatapp \ + -e JWT_SECRET=your-secret-key \ + ghcr.io/codingf0x/chat-app/backend:latest + ``` + +4. **Run Frontend:** + ```bash + docker run -d \ + --name chat-frontend \ + -p 80:80 \ + ghcr.io/codingf0x/chat-app/frontend:latest + ``` + +## GitHub Actions - Automated Builds + +The repository includes a GitHub Actions workflow that automatically: + +1. Builds Docker images when you push to `main` or `develop` branches +2. Pushes images to GitHub Container Registry (GHCR) +3. Tags images appropriately + +### How to trigger a build: + +- **Push to main/develop:** Images are built and pushed automatically +- **Create a version tag:** `git tag v1.0.0 && git push origin v1.0.0` +- **Manual trigger:** Go to Actions tab in GitHub → Select workflow → Run workflow + +### Access built images: + +Images are available at: +- `ghcr.io/codingf0x/chat-app/backend:latest` +- `ghcr.io/codingf0x/chat-app/frontend:latest` + +## Troubleshooting + +### Services won't start +```bash +# Check service status +docker-compose ps + +# View logs for errors +docker-compose logs +``` + +### Database connection issues +```bash +# Ensure MongoDB is running +docker-compose ps mongo + +# Check MongoDB logs +docker-compose logs mongo +``` + +### Port already in use +```bash +# Find what's using the port +sudo lsof -i :3000 # For backend +sudo lsof -i :80 # For frontend + +# Or change ports in docker-compose.yml +``` + +### Reset everything +```bash +# Stop and remove all containers, networks, and volumes +docker-compose down -v + +# Start fresh +docker-compose up -d +``` + +## Next Steps + +- Read [DOCKER.md](./DOCKER.md) for detailed documentation +- Configure AWS S3 for file uploads +- Set up proper JWT secrets for production +- Consider using Kubernetes for production deployment + +## Support + +For issues or questions: +- Create an issue on GitHub +- Check existing documentation in DOCKER.md +- Review Docker logs for error messages diff --git a/README.md b/README.md index a135c1e..af3ea13 100644 --- a/README.md +++ b/README.md @@ -210,3 +210,37 @@ npm run test:e2e # run end-to-end tests npm run dev # start Vite development server npm run build # production build npm run codegen # generate GraphQL types/hooks +``` + +--- + +## Docker Deployment + +The application supports Docker containerization with automated builds via GitHub Actions. + +### Quick Start with Docker + +```bash +# Using Docker Compose (recommended for local development) +docker-compose up -d + +# Or pull pre-built images from GitHub Container Registry +docker pull ghcr.io/codingf0x/chat-app/backend:latest +docker pull ghcr.io/codingf0x/chat-app/frontend:latest +``` + +### Documentation + +- **[QUICKSTART.md](./QUICKSTART.md)** - Quick setup guide for Docker +- **[DOCKER.md](./DOCKER.md)** - Comprehensive Docker deployment documentation + +### Container Registry + +Docker images are automatically built and pushed to GitHub Container Registry (GHCR) via GitHub Actions when: +- Code is pushed to `main` or `develop` branches +- Version tags are created (e.g., `v1.0.0`) +- Manually triggered via GitHub Actions UI + +**Available Images:** +- Backend: `ghcr.io/codingf0x/chat-app/backend:latest` +- Frontend: `ghcr.io/codingf0x/chat-app/frontend:latest` diff --git a/backend/.dockerignore b/backend/.dockerignore new file mode 100644 index 0000000..31f77c2 --- /dev/null +++ b/backend/.dockerignore @@ -0,0 +1,48 @@ +# Dependencies +node_modules +npm-debug.log +package-lock.json + +# Testing +coverage +.nyc_output +*.spec.ts +test + +# Build outputs +dist + +# Environment files +.env +.env.local +.env.*.local +.env.stage.prod + +# IDE +.idea +.vscode +*.swp +*.swo +*~ + +# OS +.DS_Store +Thumbs.db + +# Git +.git +.gitignore +.gitattributes + +# Documentation +README.md +*.md + +# Logs +logs +*.log + +# Misc +.prettierrc +.eslintrc.js +module-deps.png diff --git a/backend/Dockerfile b/backend/Dockerfile new file mode 100644 index 0000000..52f6be6 --- /dev/null +++ b/backend/Dockerfile @@ -0,0 +1,47 @@ +# Build stage +FROM node:20-slim AS builder + +WORKDIR /app + +# Install all dependencies by only copying package.json (not package-lock.json) +# This avoids the circular dependency issue in package-lock.json +COPY package.json ./ +RUN npm pkg delete dependencies.backend 2>/dev/null || true && \ + npm install --legacy-peer-deps + +# Copy source code +COPY . . + +# Build the application +RUN npm run build + +# Production stage +FROM node:20-slim + +WORKDIR /app + +# Create a non-root user +RUN groupadd -g 1001 nodejs && \ + useradd -r -u 1001 -g nodejs nestjs + +# Install only production dependencies +COPY package.json ./ +RUN npm pkg delete dependencies.backend 2>/dev/null || true && \ + npm install --omit=dev --legacy-peer-deps + +# Copy built application from builder stage +COPY --from=builder --chown=nestjs:nodejs /app/dist ./dist + +# Switch to non-root user +USER nestjs + +# Expose the application port +EXPOSE 3000 + +# Health check +HEALTHCHECK --interval=30s --timeout=3s --start-period=40s \ + CMD node -e "require('http').get('http://localhost:3000/health', (r) => {process.exit(r.statusCode === 200 ? 0 : 1)})" || exit 1 + +# Start the application +CMD ["node", "dist/main"] + diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..9d62ae7 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,74 @@ +version: '3.8' + +services: + # MongoDB Database + mongo: + image: mongo:latest + container_name: chat-mongo + restart: unless-stopped + ports: + - "27017:27017" + volumes: + - mongo-data:/data/db + environment: + - MONGO_INITDB_DATABASE=chatapp + healthcheck: + test: echo 'db.runCommand("ping").ok' | mongosh localhost:27017/chatapp --quiet + interval: 10s + timeout: 5s + retries: 5 + + # Backend Service + backend: + build: + context: ./backend + dockerfile: Dockerfile + container_name: chat-backend + restart: unless-stopped + ports: + - "3000:3000" + environment: + - MONGODB_URI=mongodb://mongo:27017/chatapp + - JWT_SECRET=${JWT_SECRET} + - STAGE=prod + - AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID:-your-aws-key} + - AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY:-your-aws-secret} + - S3_BUCKET_NAME=${S3_BUCKET_NAME:-your-bucket} + - JWT_EXPIRATION=3600 + - COOKIE_EXPIRATION=3600000 + depends_on: + mongo: + condition: service_healthy + healthcheck: + test: ["CMD", "node", "-e", "require('http').get('http://localhost:3000/health', (r) => {process.exit(r.statusCode === 200 ? 0 : 1)})"] + interval: 30s + timeout: 3s + retries: 3 + start_period: 40s + + # Frontend Service + frontend: + build: + context: ./frontend + dockerfile: Dockerfile + container_name: chat-frontend + restart: unless-stopped + ports: + - "80:80" + depends_on: + backend: + condition: service_healthy + healthcheck: + test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost/"] + interval: 30s + timeout: 3s + retries: 3 + start_period: 10s + +volumes: + mongo-data: + driver: local + +networks: + default: + name: chat-network diff --git a/frontend/.dockerignore b/frontend/.dockerignore new file mode 100644 index 0000000..f9c0a77 --- /dev/null +++ b/frontend/.dockerignore @@ -0,0 +1,41 @@ +# Dependencies +node_modules +npm-debug.log +package-lock.json + +# Build outputs +dist + +# Environment files +.env +.env.local +.env.*.local + +# IDE +.idea +.vscode +*.swp +*.swo +*~ + +# OS +.DS_Store +Thumbs.db + +# Git +.git +.gitignore +.gitattributes + +# Documentation +README.md +*.md + +# Logs +logs +*.log + +# Misc +.prettierrc +eslint.config.js +codegen.ts diff --git a/frontend/Dockerfile b/frontend/Dockerfile new file mode 100644 index 0000000..57b27d1 --- /dev/null +++ b/frontend/Dockerfile @@ -0,0 +1,47 @@ +# Build stage +FROM node:20-slim AS builder + +WORKDIR /app + +# Install dependencies by only copying package.json (not package-lock.json) +# This avoids the circular dependency issue in package-lock.json +COPY package.json ./ +RUN npm pkg delete dependencies.frontend 2>/dev/null || true && \ + npm install --legacy-peer-deps + +# Copy source code +COPY . . + +# Build the application +RUN npm run build + +# Production stage +FROM nginx:alpine + +# Copy custom nginx config if needed +COPY --from=builder /app/dist /usr/share/nginx/html + +# Create nginx config for SPA +RUN echo 'server { \n\ + listen 80; \n\ + location / { \n\ + root /usr/share/nginx/html; \n\ + index index.html index.htm; \n\ + try_files $uri $uri/ /index.html; \n\ + } \n\ + error_page 500 502 503 504 /50x.html; \n\ + location = /50x.html { \n\ + root /usr/share/nginx/html; \n\ + } \n\ +}' > /etc/nginx/conf.d/default.conf + +# Expose port +EXPOSE 80 + +# Health check using wget (installed in nginx:alpine) +HEALTHCHECK --interval=30s --timeout=3s --start-period=10s \ + CMD wget --quiet --tries=1 --spider http://localhost/ || exit 1 + +# Start nginx +CMD ["nginx", "-g", "daemon off;"] +