Skip to content
Merged
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
10 changes: 10 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
node_modules
npm-debug.log
Dockerfile
.dockerignore
.git
.gitignore
README.md
client
server
docker-compose.yml
18 changes: 18 additions & 0 deletions client/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Use official Nginx image
FROM nginx:alpine

# Remove default Nginx website
RUN rm -rf /usr/share/nginx/html/*

# Copy static files from public directory to Nginx html root
# Note: This copies the contents of 'public' directly to '/usr/share/nginx/html'
COPY public/ /usr/share/nginx/html/

# Copy custom Nginx configuration
COPY client/nginx.conf /etc/nginx/conf.d/default.conf

# Expose port 80
EXPOSE 80

# Start Nginx
CMD ["nginx", "-g", "daemon off;"]
40 changes: 40 additions & 0 deletions client/nginx.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
server {
listen 80;
server_name localhost;

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

# Serve static files from root
# Serve static files from root
location / {
try_files $uri $uri/ $uri.html /index.html;

# Security Headers (Matching Helmet config from server.js)
add_header X-Frame-Options "SAMEORIGIN";
add_header X-Content-Type-Options "nosniff";
add_header Referrer-Policy "strict-origin-when-cross-origin";
# CSP is complex to replicate exactly in Nginx without dynamic values,
# but we add a baseline here. The backend API responses will still carry their own CSP.
add_header Content-Security-Policy "default-src 'self' https: data: blob: 'unsafe-inline' 'unsafe-eval'; img-src 'self' data: https:; font-src 'self' https: data:;";
}

# Proxy API requests to the backend service
location /api/ {
proxy_pass http://backend:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}

# Proxy Socket.IO requests
location /socket.io/ {
proxy_pass http://backend:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
}
}
46 changes: 40 additions & 6 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,9 +1,43 @@
version: "3.9"
version: '3.8'

services:
expenseflow:
build: .
image: expenseflow:latest
# Frontend Application (Nginx)
frontend:
build:
context: .
dockerfile: client/Dockerfile
ports:
- "8080:80"
restart: unless-stopped
- "3000:80"
depends_on:
- backend
restart: always

# Backend Application (Node.js/Express)
backend:
build:
context: .
dockerfile: server/Dockerfile
ports:
- "3001:3000" # Expose on 3001 to avoid conflict with frontend on host
environment:
- PORT=3000
- MONGODB_URI=mongodb://mongo:27017/expenseflow
- NODE_ENV=development
- FRONTEND_URL=http://localhost:3000
env_file:
- .env
depends_on:
- mongo
restart: always

# Database (MongoDB)
mongo:
image: mongo:latest
ports:
- "27017:27017"
volumes:
- mongo-data:/data/db
restart: always

volumes:
mongo-data:
20 changes: 20 additions & 0 deletions server/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Use official Node.js image
FROM node:18-alpine

# Set working directory
WORKDIR /app

# Copy package files first for better caching
COPY package*.json ./

# Install dependencies
RUN npm install

# Copy the rest of the application code
COPY . .

# Expose the port the app runs on
EXPOSE 3000

# Start the application
CMD ["npm", "run", "dev"]