Added docker compose deployments

This commit is contained in:
Geert Rademakes 2025-04-24 15:46:10 +02:00
parent 901c78990b
commit 3e8141aeba
7 changed files with 160 additions and 4 deletions

47
docker-compose.yml Normal file
View File

@ -0,0 +1,47 @@
version: '3.8'
services:
frontend:
build:
context: .
dockerfile: packages/frontend/Dockerfile
ports:
- "8080:80"
environment:
- VITE_API_URL=http://localhost:3001/api
depends_on:
backend:
condition: service_healthy
restart: unless-stopped
backend:
build:
context: .
dockerfile: packages/backend/Dockerfile
ports:
- "3001:3000"
environment:
- MONGODB_URI=mongodb://mongo:27017/rekordbox
- PORT=3000
- NODE_ENV=production
depends_on:
mongo:
condition: service_healthy
restart: unless-stopped
mongo:
image: mongo:latest
ports:
- "27017:27017"
volumes:
- mongodb_data:/data/db
healthcheck:
test: ["CMD", "mongosh", "--eval", "db.adminCommand('ping')"]
interval: 30s
timeout: 10s
retries: 3
start_period: 20s
restart: unless-stopped
volumes:
mongodb_data:

View File

@ -0,0 +1,38 @@
FROM node:20-alpine as builder
WORKDIR /app
# Copy root package files
COPY package*.json ./
COPY packages/backend/package*.json ./packages/backend/
# Install dependencies
RUN npm install
# Copy source code
COPY packages/backend/ ./packages/backend/
# Build the app
RUN cd packages/backend && npm run build
# Production image
FROM node:20-alpine
WORKDIR /app
# Copy package files
COPY packages/backend/package*.json ./
# Install production dependencies only
RUN npm install --production
# Copy built files from builder stage
COPY --from=builder /app/packages/backend/dist ./dist
# Add health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD wget --quiet --tries=1 --spider http://localhost:3000/api/health || exit 1
EXPOSE 3000
CMD ["node", "dist/index.js"]

View File

@ -13,6 +13,16 @@ const port = process.env.PORT || 3000;
app.use(cors());
app.use(express.json());
// Health check endpoint
app.get('/api/health', (req, res) => {
const mongoStatus = mongoose.connection.readyState === 1 ? 'connected' : 'disconnected';
res.json({
status: 'ok',
timestamp: new Date().toISOString(),
mongo: mongoStatus
});
});
// Connect to MongoDB
mongoose.connect(process.env.MONGODB_URI || 'mongodb://localhost:27017/rekordbox')
.then(() => console.log('Connected to MongoDB'))

View File

@ -47,7 +47,4 @@ const songSchema = new mongoose.Schema({
}
});
// Ensure index on id field
songSchema.index({ id: 1 }, { unique: true });
export const Song = mongoose.model('Song', songSchema);

View File

@ -0,0 +1,31 @@
FROM node:20-alpine as builder
WORKDIR /app
# Copy root package files
COPY package*.json ./
COPY packages/frontend/package*.json ./packages/frontend/
# Install dependencies
RUN npm install
# Copy source code
COPY packages/frontend/ ./packages/frontend/
# Build the app
RUN cd packages/frontend && npm run build
# Production image
FROM nginx:alpine
# Copy nginx configuration
COPY packages/frontend/nginx.conf /etc/nginx/conf.d/default.conf
# Copy built files from builder stage
COPY --from=builder /app/packages/frontend/dist /usr/share/nginx/html
# Add health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD wget --quiet --tries=1 --spider http://localhost:80/ || exit 1
EXPOSE 80

View File

@ -0,0 +1,33 @@
server {
listen 80;
server_name localhost;
# Security headers
add_header X-Frame-Options "SAMEORIGIN";
add_header X-XSS-Protection "1; mode=block";
add_header X-Content-Type-Options "nosniff";
# Gzip compression
gzip on;
gzip_vary on;
gzip_min_length 10240;
gzip_proxied expired no-cache no-store private auth;
gzip_types text/plain text/css text/xml text/javascript application/x-javascript application/xml application/javascript;
gzip_disable "MSIE [1-6]\.";
root /usr/share/nginx/html;
index index.html;
# Cache static assets
location /assets {
expires 1y;
add_header Cache-Control "public, no-transform";
}
# Handle SPA routing
location / {
try_files $uri $uri/ /index.html;
expires -1;
add_header Cache-Control "no-store, no-cache, must-revalidate";
}
}

View File

@ -1,6 +1,6 @@
import { Song, Playlist } from '../types/interfaces';
const API_URL = 'http://localhost:3000/api';
const API_URL = import.meta.env.VITE_API_URL || 'http://localhost:3000/api';
async function handleResponse<T>(response: Response): Promise<T> {
if (!response.ok) {