Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions .github/workflows/cicd-kind.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
name: CI/CD → Docker Hub + Kind (Kubernetes)

on:
push:
branches: [ main ]

jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}

- name: Build & Push Backend
run: |
docker build -t ${{ secrets.DOCKERHUB_USERNAME }}/prepcheck-backend:latest ./backend
docker push ${{ secrets.DOCKERHUB_USERNAME }}/prepcheck-backend:latest

- name: Build & Push Frontend
run: |
docker build -t ${{ secrets.DOCKERHUB_USERNAME }}/prepcheck-frontend:latest ./frontend
docker push ${{ secrets.DOCKERHUB_USERNAME }}/prepcheck-frontend:latest

- name: Create Kind cluster
uses: helm/kind-action@v1.10.0
with:
install_local_path_provisioner: true

- name: Deploy to Kind
run: |
kubectl create namespace prepcheck --dry-run=client -o yaml | kubectl apply -f -
kubectl apply -f k8s/ -n prepcheck
echo "Deployment complete! Access at http://prepcheck.local (add to /etc/hosts)"

- name: Show running pods
run: kubectl get pods -n prepcheck -o wide
175 changes: 175 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -485,3 +485,178 @@ If you encounter any issues:

<h3>Made with 💜 by CloudCTRL.</h3>

PrepCheck is an AI-powered career readiness screener application designed to analyze CVs and provide insightful feedback. It consists of a modern React frontend and a Node.js/Express backend integrated with Google Gemini AI for advanced CV analysis.

---

## Table of Contents

- [App Overview](#app-overview)
- [Technologies](#technologies)
- [Features](#features)
- [Getting Started - Development Setup](#getting-started---development-setup)
- [Testing](#testing)
- [Docker & Containerization](#docker--containerization)
- [CI/CD Pipeline](#cicd-pipeline)
- [Hosting](#hosting)
- [License](#license)

---

## App Overview

The PrepCheck application allows users to upload their CVs/resumes in PDF or DOCX format for AI-powered parsing and analysis. The backend extracts the textual content from the CV and sends it to Google Gemini AI for analysis to deliver personalized career readiness insights. Additionally, the app features a chat assistant for interactive querying.

The frontend is a React-based UI built with Vite and Tailwind CSS, providing a performant and responsive user experience including visualization and document upload.

---

## Technologies

### Backend
- Node.js with Express framework
- File upload handling with Multer
- CV parsing with `pdf-parse` and `mammoth` (for DOCX)
- AI integration via Google Generative AI SDK
- Environment configuration with `dotenv`
- Testing with Jest and Supertest

### Frontend
- React 18 with Vite as the build tool
- Styling with Tailwind CSS
- HTTP requests with Axios
- Animations using Framer Motion
- Visualization using Recharts
- PDF generation using jsPDF
- Tested with relevant unit and e2e testing tools

---

## Features

- **CV Upload and Analysis:** Upload PDF or DOCX CV files for AI-driven analysis.
- **AI Chat Assistant:** Interactive chat for career-related questions.
- **Health Check API:** Endpoint to verify backend server status.
- **Robust Validation:** File size limits and format validation (max 10MB).
- **Responsive Frontend:** Fast, intuitive user interface with charts and dashboards.

---

## Getting Started - Development Setup

### Backend

1. Navigate to the `backend` directory:
```bash
cd backend
```
2. Install dependencies:
```bash
npm install
```
3. Start the development server with hot reload:
```bash
npm run dev
```
4. The backend server will run by default on `http://localhost:5000`.

### Frontend

1. Navigate to the `frontend` directory:
```bash
cd frontend
```
2. Install dependencies:
```bash
npm install
```
3. Start the development server:
```bash
npm run dev
```
4. The frontend app will run by default on `http://localhost:5173`.

---

## Testing

### Backend Tests

- Located in `backend/tests/`
- Run backend tests using Jest:
```bash
npm test
```

### Frontend Tests

- Located under `frontend/tests/`
- Use your preferred test runner as configured for React/Vite stack.

---

## Docker & Containerization

PrepCheck uses Docker to containerize both the backend and frontend for easy deployment and consistent environment:

- **Backend Dockerfile:**
- Uses `node:22-alpine` base image.
- Installs dependencies with production optimizations.
- Exposes port `5000`.
- Starts with `node server.js`.

- **Frontend Dockerfile:**
- Multi-stage build with `node:22-alpine` for building the React app via Vite.
- Production image served by `nginx:stable-alpine`.
- Exposes port `80`.

- **docker-compose.yaml** orchestrates both services:
- Backend listens on host port `6000` mapping to container port `5000`.
- Frontend serves on host port `80`.
- Frontend service waits on backend healthcheck to ensure readiness.

To build and run containers locally with Docker Compose:

```bash
docker-compose up --build
```

---

## CI/CD Pipeline

PrepCheck leverages GitHub Actions for continuous integration and deployment:

- The workflow triggers on pushes and pull requests to the `main` branch.
- Uses Docker Buildx to build and push the backend Docker image.
- Pushes to Docker Hub repository `mmeligab/prepcheck-backend`.
- Configured with caching for efficient image builds.
- Authentication uses GitHub Secrets for Docker Hub credentials.

This pipeline ensures automated testing, building, and pushing of Docker images on update.

---

## Hosting

The application is designed to be hosted in a Docker-compatible environment:

- Run backend and frontend as separate containers managed via `docker-compose`.
- Backend exposed on port `6000` on the host.
- Frontend exposed on port `80` and depends on the backend being healthy before starting.
- Suitable for deployment on cloud platforms supporting Docker (e.g., AWS ECS, Azure Container Instances, DigitalOcean, or a VPS server with Docker installed).

---
## Devs

- Tech Lead Frontend / Backend: [Nqobile](https://nqobile-portfolio.onrender.com/)
- Writer: [Boipelo](https://github.com/boipelo-codes)
- DevOps: [Mmeli](https://personal-site-zeta-silk.vercel.app/)

---

## License

This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details.

---
28 changes: 23 additions & 5 deletions docker-compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,39 @@ services:
backend:
build:
context: ./backend
dockerfile: Dockerfile
ports:
- "6000:5000" # Host port 6000 → container port 5000 (avoids conflicts)
- "6000:5000" # only needed for local API testing
environment:
NODE_ENV: production
- NODE_ENV=production
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:5000/api/health"]
interval: 5s
interval: 10s
timeout: 5s
retries: 5
start_period: 5s
start_period: 10s
networks:
- app-network

frontend:
build:
context: ./frontend
dockerfile: Dockerfile
args:
# This becomes import.meta.env.VITE_API_BASE_URL in your React code
VITE_API_BASE_URL: /api
ports:
- "80:80"
- "8080:80" # ← NOW CORRECT: Nginx listens on 80 inside container
depends_on:
backend:
condition: service_healthy
# In production Docker, the frontend does NOT need the Vite proxy anymore.
# Instead we let Nginx proxy /api calls to the backend service:
environment:
- NGINX_BACKEND_HOST=backend # used in nginx.conf template (see below)
networks:
- app-network

networks:
app-network:
driver: bridge
21 changes: 9 additions & 12 deletions frontend/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,20 +1,17 @@
# Build stage
FROM node:22-alpine AS build

FROM node:20-alpine AS builder
WORKDIR /app

COPY package*.json ./
RUN npm ci

COPY . .

# Build the Vite/React app
ARG VITE_API_BASE_URL=/api
ENV VITE_API_BASE_URL=${VITE_API_BASE_URL}
RUN npm run build

# Production stage (serve static files)
FROM nginx:stable-alpine AS production
COPY --from=build /app/dist /usr/share/nginx/html

# Production stage
FROM nginx:alpine-slim
COPY --from=builder /app/dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80

CMD ["nginx", "-g", "daemon off;"]
HEALTHCHECK CMD wget -qO- http://localhost/health || exit 1
CMD ["nginx", "-g", "daemon off;"]
37 changes: 37 additions & 0 deletions frontend/nginx.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
server {
listen 80;
server_name localhost;

root /usr/share/nginx/html;
index index.html;

# Serve the React app + SPA fallback
location / {
try_files $uri $uri/ /index.html;
}

# Proxy all /api calls to the backend container (Docker DNS resolves "backend")
location /api/ {
proxy_pass http://backend:5000/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}

# Optional: explicit health endpoint for frontend
location /health {
access_log off;
return 200 "healthy\n";
add_header Content-Type text/plain;
}

# Security + performance headers
add_header Cache-Control "no-store, no-cache, must-revalidate";
add_header X-Content-Type-Options nosniff;
add_header X-Frame-Options DENY;
add_header X-XSS-Protection "1; mode=block";

gzip on;
gzip_types text/css application/javascript text/javascript application/json;
}
2 changes: 1 addition & 1 deletion frontend/vite.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export default defineConfig({
port: 3000,
proxy: {
'/api': {
target: 'http://localhost:5000',
target: "https://prepcheck-sg01.onrender.com" || 'http://localhost:5000',
changeOrigin: true,
}
}
Expand Down
30 changes: 30 additions & 0 deletions k8s/backend-deployment.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: backend
namespace: prepcheck
spec:
replicas: 2
selector:
matchLabels:
app: backend
template:
metadata:
labels:
app: backend
spec:
containers:
- name: backend
image: YOUR_DOCKERHUB_USERNAME/prepcheck-backend:latest
ports:
- containerPort: 5000
env:
- name: NODE_ENV
value: production
resources:
limits:
cpu: "500m"
memory: "512Mi"
requests:
cpu: "200m"
memory: "256Mi"
11 changes: 11 additions & 0 deletions k8s/backend-service.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
apiVersion: v1
kind: Service
metadata:
name: backend
namespace: prepcheck
spec:
selector:
app: backend
ports:
- port: 5000
targetPort: 5000
Loading
Loading