Rewrite docker-compose for staging; fix uploads proxy to use BACKEND_URL HTTP
This commit is contained in:
parent
50a54255a8
commit
beaaf1b99a
2 changed files with 36 additions and 158 deletions
|
|
@ -1,67 +1,53 @@
|
|||
version: '3.8'
|
||||
|
||||
services:
|
||||
# Frontend service
|
||||
frontend:
|
||||
build:
|
||||
context: ./frontend
|
||||
dockerfile: Dockerfile
|
||||
restart: unless-stopped
|
||||
user: node
|
||||
ports:
|
||||
- "3000:3000"
|
||||
depends_on:
|
||||
- backend
|
||||
env_file: ./frontend/.env
|
||||
volumes:
|
||||
- ./logs:/app/logs:rw
|
||||
backend:
|
||||
condition: service_healthy
|
||||
environment:
|
||||
- BACKEND_URL=http://backend:4000
|
||||
- NODE_ENV=production
|
||||
networks:
|
||||
- app-network
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
|
||||
test: ["CMD", "wget", "-qO-", "http://localhost:3000"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
memory: 512M
|
||||
cpus: '0.5'
|
||||
|
||||
# Backend API service
|
||||
backend:
|
||||
build:
|
||||
context: ./backend
|
||||
dockerfile: Dockerfile
|
||||
restart: unless-stopped
|
||||
user: node
|
||||
ports:
|
||||
- "4000:4000"
|
||||
depends_on:
|
||||
- mongodb
|
||||
- redis
|
||||
mongodb:
|
||||
condition: service_healthy
|
||||
redis:
|
||||
condition: service_healthy
|
||||
env_file: ./backend/.env
|
||||
volumes:
|
||||
- ./uploads:/app/uploads:rw
|
||||
- ./logs:/app/logs:rw
|
||||
networks:
|
||||
- app-network
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:4000/health"]
|
||||
test: ["CMD", "wget", "-qO-", "http://localhost:4000/health"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
memory: 512M
|
||||
cpus: '0.5'
|
||||
|
||||
# MongoDB service
|
||||
mongodb:
|
||||
image: mongo:7.0
|
||||
restart: unless-stopped
|
||||
user: mongodb
|
||||
volumes:
|
||||
- mongo-data:/data/db:rw
|
||||
- ./mongo-init:/docker-entrypoint-initdb.d:ro
|
||||
|
|
@ -71,84 +57,32 @@ services:
|
|||
environment:
|
||||
- MONGO_INITDB_ROOT_USERNAME=${MONGO_USER}
|
||||
- MONGO_INITDB_ROOT_PASSWORD=${MONGO_PASSWORD}
|
||||
command: ["--auth", "--bind_ip_all", "--tlsMode", "preferTLS"]
|
||||
command: ["--auth", "--bind_ip_all"]
|
||||
healthcheck:
|
||||
test: echo 'db.runCommand("ping").ok' | mongosh localhost:27017 --quiet
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
memory: 1G
|
||||
cpus: '1.0'
|
||||
retries: 5
|
||||
|
||||
# Redis service
|
||||
redis:
|
||||
image: redis:alpine
|
||||
restart: unless-stopped
|
||||
command: ["redis-server", "--requirepass", "${REDIS_PASSWORD}"]
|
||||
env_file: ./backend/.env
|
||||
volumes:
|
||||
- redis-data:/data:rw
|
||||
networks:
|
||||
- app-network
|
||||
healthcheck:
|
||||
test: ["CMD", "redis-cli", "ping"]
|
||||
test: ["CMD", "redis-cli", "-a", "${REDIS_PASSWORD}", "ping"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
memory: 256M
|
||||
cpus: '0.2'
|
||||
|
||||
# Nginx service for production
|
||||
nginx:
|
||||
image: nginx:alpine
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "80:80"
|
||||
- "443:443"
|
||||
volumes:
|
||||
- ./nginx/conf:/etc/nginx/conf.d:ro
|
||||
- ./nginx/ssl:/etc/nginx/ssl:ro
|
||||
- ./frontend/public:/var/www/html:ro
|
||||
depends_on:
|
||||
- frontend
|
||||
- backend
|
||||
networks:
|
||||
- app-network
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "https://localhost"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
memory: 128M
|
||||
cpus: '0.1'
|
||||
|
||||
# Security scanner
|
||||
security_scanner:
|
||||
image: aquasec/trivy
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
- ./security-reports:/reports
|
||||
command: ["image", "--format", "table", "--output", "/reports/scan-$(date +%Y%m%d).txt", "lad-website-frontend:latest", "lad-website-backend:latest"]
|
||||
profiles:
|
||||
- security
|
||||
|
||||
# Networks with isolation
|
||||
networks:
|
||||
app-network:
|
||||
driver: bridge
|
||||
ipam:
|
||||
config:
|
||||
- subnet: 172.20.0.0/24
|
||||
|
||||
# Volumes with backup capability
|
||||
volumes:
|
||||
mongo-data:
|
||||
name: lad-mongo-data
|
||||
|
|
|
|||
|
|
@ -1,91 +1,35 @@
|
|||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import path from 'path';
|
||||
import fs from 'fs';
|
||||
|
||||
/**
|
||||
* API route to serve uploaded files
|
||||
* This allows us to serve files from the backend uploads directory
|
||||
*/
|
||||
export async function GET(
|
||||
request: NextRequest,
|
||||
{ params }: { params: Promise<{ path: string[] }> }
|
||||
) {
|
||||
const { path: pathSegments } = await params;
|
||||
const filePath = pathSegments.join('/').replace(/^uploads\//, '');
|
||||
const backendUrl = process.env.BACKEND_URL ?? 'http://localhost:4000';
|
||||
|
||||
try {
|
||||
const { path: pathSegments } = await params;
|
||||
const filePath = pathSegments.join('/');
|
||||
const upstream = await fetch(`${backendUrl}/uploads/${filePath}`, {
|
||||
next: { revalidate: 86400 },
|
||||
});
|
||||
|
||||
// Construct the absolute path to the file
|
||||
// This assumes the backend is in the same directory as the frontend
|
||||
// Check if filePath already starts with 'uploads/'
|
||||
const absolutePath = filePath.startsWith('uploads/')
|
||||
? path.join(process.cwd(), '..', 'backend', filePath)
|
||||
: path.join(process.cwd(), '..', 'backend', 'uploads', filePath);
|
||||
|
||||
console.log('Requested file path:', filePath);
|
||||
console.log('Absolute path:', absolutePath);
|
||||
|
||||
// Check if the file exists
|
||||
if (!fs.existsSync(absolutePath)) {
|
||||
return new NextResponse('File not found', { status: 404 });
|
||||
if (!upstream.ok) {
|
||||
return new NextResponse('File not found', { status: upstream.status });
|
||||
}
|
||||
|
||||
// Read the file
|
||||
const fileBuffer = fs.readFileSync(absolutePath);
|
||||
const buffer = await upstream.arrayBuffer();
|
||||
const contentType = upstream.headers.get('content-type') ?? 'application/octet-stream';
|
||||
const filename = path.basename(filePath);
|
||||
|
||||
// Determine content type based on file extension
|
||||
const ext = path.extname(filePath).toLowerCase();
|
||||
let contentType = 'application/octet-stream'; // Default content type
|
||||
|
||||
// Set content type based on file extension
|
||||
switch (ext) {
|
||||
case '.mp4':
|
||||
contentType = 'video/mp4';
|
||||
break;
|
||||
case '.webm':
|
||||
contentType = 'video/webm';
|
||||
break;
|
||||
case '.jpg':
|
||||
case '.jpeg':
|
||||
contentType = 'image/jpeg';
|
||||
break;
|
||||
case '.png':
|
||||
contentType = 'image/png';
|
||||
break;
|
||||
case '.gif':
|
||||
contentType = 'image/gif';
|
||||
break;
|
||||
case '.vtt':
|
||||
contentType = 'text/vtt';
|
||||
break;
|
||||
case '.srt':
|
||||
contentType = 'text/plain';
|
||||
break;
|
||||
case '.txt':
|
||||
contentType = 'text/plain';
|
||||
break;
|
||||
case '.pdf':
|
||||
contentType = 'application/pdf';
|
||||
break;
|
||||
case '.doc':
|
||||
case '.docx':
|
||||
contentType = 'application/msword';
|
||||
break;
|
||||
case '.ppt':
|
||||
case '.pptx':
|
||||
contentType = 'application/vnd.ms-powerpoint';
|
||||
break;
|
||||
}
|
||||
|
||||
// Return the file with appropriate headers
|
||||
return new NextResponse(fileBuffer, {
|
||||
return new NextResponse(buffer, {
|
||||
headers: {
|
||||
'Content-Type': contentType,
|
||||
'Content-Disposition': `inline; filename="${path.basename(filePath)}"`,
|
||||
'Cache-Control': 'public, max-age=86400' // Cache for 1 day
|
||||
}
|
||||
'Content-Disposition': `inline; filename="${filename}"`,
|
||||
'Cache-Control': 'public, max-age=86400',
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Error serving file:', error);
|
||||
return new NextResponse('Internal Server Error', { status: 500 });
|
||||
} catch {
|
||||
return new NextResponse('Upstream unavailable', { status: 502 });
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue