Initial commit
This commit is contained in:
@@ -0,0 +1,55 @@
|
||||
# Multi-stage Dockerfile para Produção
|
||||
# Uso: docker build -f docker/production/Dockerfile -t course-plat .
|
||||
|
||||
FROM node:20-alpine AS base
|
||||
|
||||
# Instalar dependências apenas para produção
|
||||
FROM base AS deps
|
||||
RUN apk add --no-cache libc6-compat
|
||||
WORKDIR /app
|
||||
|
||||
# Copiar package files
|
||||
COPY package.json package-lock.json* ./
|
||||
RUN npm ci --only=production
|
||||
|
||||
# Build da aplicação
|
||||
FROM base AS builder
|
||||
WORKDIR /app
|
||||
COPY --from=deps /app/node_modules ./node_modules
|
||||
COPY . .
|
||||
|
||||
# Build Next.js
|
||||
ENV NEXT_TELEMETRY_DISABLED=1
|
||||
RUN npm run build
|
||||
|
||||
# Imagem final de produção
|
||||
FROM base AS runner
|
||||
WORKDIR /app
|
||||
|
||||
ENV NODE_ENV=production
|
||||
ENV NEXT_TELEMETRY_DISABLED=1
|
||||
|
||||
# Criar usuário não-root
|
||||
RUN addgroup --system --gid 1001 nodejs
|
||||
RUN adduser --system --uid 1001 nextjs
|
||||
|
||||
# Copiar arquivos necessários
|
||||
COPY --from=builder /app/public ./public
|
||||
COPY --from=builder /app/.next/standalone ./
|
||||
COPY --from=builder /app/.next/static ./.next/static
|
||||
|
||||
# Criar diretório para storage local
|
||||
RUN mkdir -p /app/storage/uploads && chown -R nextjs:nodejs /app/storage
|
||||
|
||||
USER nextjs
|
||||
|
||||
EXPOSE 3000
|
||||
|
||||
ENV PORT=3000
|
||||
ENV HOSTNAME="0.0.0.0"
|
||||
|
||||
# Health check
|
||||
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
|
||||
CMD wget --no-verbose --tries=1 --spider http://localhost:3000/api/health || exit 1
|
||||
|
||||
CMD ["node", "server.js"]
|
||||
Executable
+136
@@ -0,0 +1,136 @@
|
||||
#!/bin/bash
|
||||
# ==========================================
|
||||
# BACKUP MONGODB - PRODUÇÃO
|
||||
# ==========================================
|
||||
# Suporta MongoDB nativo ou Docker
|
||||
# Uso: ./backup-mongodb.sh
|
||||
#
|
||||
# Adicionar ao crontab (deployuser):
|
||||
# 0 2 * * * cd /var/www/iciv && ./docker/production/backup-mongodb.sh >> ~/backup-mongodb.log 2>&1
|
||||
|
||||
set -e
|
||||
|
||||
# ==========================================
|
||||
# CONFIGURAÇÕES
|
||||
# ==========================================
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
PROJECT_DIR="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
||||
|
||||
BACKUP_DIR="${BACKUP_DIR:-$PROJECT_DIR/backups/mongodb}"
|
||||
|
||||
RETENTION_DAYS="${RETENTION_DAYS:-7}"
|
||||
|
||||
CONTAINER_NAME="${MONGO_CONTAINER:-course-plat-mongodb}"
|
||||
|
||||
ENV_FILE="${ENV_FILE:-$PROJECT_DIR/.env.production}"
|
||||
if [ -f "$ENV_FILE" ]; then
|
||||
set -a
|
||||
source <(grep -v '^\s*#' "$ENV_FILE")
|
||||
set +a
|
||||
fi
|
||||
|
||||
MONGO_USERNAME="${MONGO_USERNAME:-admin}"
|
||||
MONGO_PASSWORD="${MONGO_PASSWORD:-password}"
|
||||
MONGO_DB="${MONGO_DB:-course-plat}"
|
||||
|
||||
# ==========================================
|
||||
# FUNÇÕES
|
||||
# ==========================================
|
||||
|
||||
log() {
|
||||
echo "[$(date +'%Y-%m-%d %H:%M:%S')] $1"
|
||||
}
|
||||
|
||||
error() {
|
||||
log "ERROR: $1" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
build_mongo_uri() {
|
||||
if [ -n "$MONGO_USERNAME" ] && [ -n "$MONGO_PASSWORD" ]; then
|
||||
echo "mongodb://${MONGO_USERNAME}:${MONGO_PASSWORD}@127.0.0.1:27017/${MONGO_DB}?authSource=admin"
|
||||
else
|
||||
echo "mongodb://127.0.0.1:27017/${MONGO_DB}"
|
||||
fi
|
||||
}
|
||||
|
||||
# ==========================================
|
||||
# DETECÇÃO: Docker ou nativo
|
||||
# ==========================================
|
||||
|
||||
DOCKER_CMD="$(command -v docker 2>/dev/null || true)"
|
||||
|
||||
USE_DOCKER=false
|
||||
if [ -n "$DOCKER_CMD" ] && "$DOCKER_CMD" ps 2>/dev/null | grep -q "$CONTAINER_NAME"; then
|
||||
USE_DOCKER=true
|
||||
log "Modo: Docker (container: $CONTAINER_NAME)"
|
||||
else
|
||||
MONGODUMP="$(command -v mongodump 2>/dev/null || true)"
|
||||
if [ -z "$MONGODUMP" ]; then
|
||||
error "mongodump nao encontrado. Instale: sudo apt install mongo-tools"
|
||||
fi
|
||||
log "Modo: MongoDB nativo"
|
||||
fi
|
||||
|
||||
# ==========================================
|
||||
# INÍCIO
|
||||
# ==========================================
|
||||
|
||||
log "Iniciando backup do MongoDB..."
|
||||
|
||||
mkdir -p "$BACKUP_DIR"
|
||||
|
||||
TIMESTAMP=$(date +%Y%m%d-%H%M%S)
|
||||
BACKUP_FILE="$BACKUP_DIR/mongodb-$TIMESTAMP.gz"
|
||||
|
||||
MONGO_URI=$(build_mongo_uri)
|
||||
log "Executando mongodump..."
|
||||
|
||||
if [ "$USE_DOCKER" = true ]; then
|
||||
"$DOCKER_CMD" exec "$CONTAINER_NAME" mongodump \
|
||||
--uri="$MONGO_URI" \
|
||||
--archive \
|
||||
--gzip \
|
||||
> "$BACKUP_FILE" 2>&1 || error "Falha no mongodump (docker)"
|
||||
else
|
||||
"$MONGODUMP" \
|
||||
--uri="$MONGO_URI" \
|
||||
--archive="$BACKUP_FILE" \
|
||||
--gzip \
|
||||
2>&1 || error "Falha no mongodump"
|
||||
fi
|
||||
|
||||
if [ ! -f "$BACKUP_FILE" ]; then
|
||||
error "Arquivo de backup nao foi criado"
|
||||
fi
|
||||
|
||||
BACKUP_SIZE=$(du -h "$BACKUP_FILE" | cut -f1)
|
||||
log "Backup concluido: $BACKUP_FILE ($BACKUP_SIZE)"
|
||||
|
||||
# ==========================================
|
||||
# LIMPEZA
|
||||
# ==========================================
|
||||
|
||||
log "Removendo backups antigos (mais de $RETENTION_DAYS dias)..."
|
||||
|
||||
REMOVED=$(find "$BACKUP_DIR" \
|
||||
-name "mongodb-*.gz" \
|
||||
-type f \
|
||||
-mtime +$RETENTION_DAYS \
|
||||
-print -delete 2>/dev/null | wc -l)
|
||||
|
||||
log "Backups removidos: $REMOVED"
|
||||
|
||||
# ==========================================
|
||||
# RESUMO
|
||||
# ==========================================
|
||||
|
||||
TOTAL_BACKUPS=$(find "$BACKUP_DIR" -name "mongodb-*.gz" -type f | wc -l)
|
||||
TOTAL_SIZE=$(du -sh "$BACKUP_DIR" 2>/dev/null | cut -f1)
|
||||
|
||||
log "Resumo:"
|
||||
log " - Backup atual: $BACKUP_FILE"
|
||||
log " - Total de backups: $TOTAL_BACKUPS"
|
||||
log " - Tamanho total: $TOTAL_SIZE"
|
||||
log "Backup do MongoDB concluido com sucesso!"
|
||||
@@ -0,0 +1,125 @@
|
||||
# Docker Compose para Produção
|
||||
# Uso: docker compose -f docker/production/docker-compose.yml up -d
|
||||
#
|
||||
# Antes de usar:
|
||||
# 1. Copiar .env.example para .env.production
|
||||
# 2. Preencher todas as variáveis de ambiente
|
||||
# 3. Buildar a imagem da aplicação: docker build -t course-plat .
|
||||
# 4. Ou usar build automático com compose (descomentar build section)
|
||||
|
||||
services:
|
||||
# Aplicação Next.js
|
||||
app:
|
||||
image: course-plat:latest
|
||||
# Para build automático, descomente abaixo:
|
||||
# build:
|
||||
# context: ../..
|
||||
# dockerfile: docker/production/Dockerfile
|
||||
container_name: course-plat-app
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "${APP_PORT:-3000}:3000"
|
||||
environment:
|
||||
# Node
|
||||
NODE_ENV: production
|
||||
|
||||
# Next.js
|
||||
NEXTAUTH_URL: ${NEXTAUTH_URL}
|
||||
NEXTAUTH_SECRET: ${NEXTAUTH_SECRET}
|
||||
|
||||
# MongoDB
|
||||
MONGO_URI: mongodb://${MONGO_USERNAME}:${MONGO_PASSWORD}@mongodb:27017/${MONGO_DB}?authSource=admin
|
||||
|
||||
# Storage
|
||||
STORAGE_TYPE: ${STORAGE_TYPE:-s3}
|
||||
LOCAL_STORAGE_PATH: /app/storage/uploads
|
||||
S3_ENDPOINT: ${S3_ENDPOINT}
|
||||
S3_REGION: ${S3_REGION:-us-east-1}
|
||||
S3_ACCESS_KEY: ${S3_ACCESS_KEY}
|
||||
S3_SECRET_KEY: ${S3_SECRET_KEY}
|
||||
S3_BUCKET: ${S3_BUCKET}
|
||||
volumes:
|
||||
- app_storage:/app/storage/uploads
|
||||
depends_on:
|
||||
- mongodb
|
||||
- minio
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:3000/api/health"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
networks:
|
||||
- course-plat-network
|
||||
|
||||
# MongoDB - Banco de dados
|
||||
mongodb:
|
||||
image: mongo:8
|
||||
container_name: course-plat-mongodb
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
MONGO_INITDB_ROOT_USERNAME: ${MONGO_USERNAME}
|
||||
MONGO_INITDB_ROOT_PASSWORD: ${MONGO_PASSWORD}
|
||||
MONGO_INITDB_DATABASE: ${MONGO_DB}
|
||||
volumes:
|
||||
- mongodb_data:/data/db
|
||||
- mongodb_config:/data/configdb
|
||||
- ./backups/mongodb:/backups # Para restore
|
||||
healthcheck:
|
||||
test: echo 'db.runCommand("ping").ok' | mongosh localhost:27017/test --quiet
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 5
|
||||
networks:
|
||||
- course-plat-network
|
||||
|
||||
# MinIO - Armazenamento de arquivos
|
||||
minio:
|
||||
image: minio/minio:latest
|
||||
container_name: course-plat-minio
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
MINIO_ROOT_USER: ${MINIO_ROOT_USER}
|
||||
MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD}
|
||||
volumes:
|
||||
- minio_data:/data
|
||||
command: server /data --console-address ":9001"
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"]
|
||||
interval: 30s
|
||||
timeout: 20s
|
||||
retries: 3
|
||||
networks:
|
||||
- course-plat-network
|
||||
|
||||
# Nginx (opcional) - Reverse Proxy
|
||||
nginx:
|
||||
image: nginx:alpine
|
||||
container_name: course-plat-nginx
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "80:80"
|
||||
- "443:443"
|
||||
volumes:
|
||||
- ./nginx.conf:/etc/nginx/nginx.conf:ro
|
||||
- ./ssl:/etc/nginx/ssl:ro
|
||||
- nginx_logs:/var/log/nginx
|
||||
depends_on:
|
||||
- app
|
||||
networks:
|
||||
- course-plat-network
|
||||
|
||||
volumes:
|
||||
mongodb_data:
|
||||
driver: local
|
||||
mongodb_config:
|
||||
driver: local
|
||||
minio_data:
|
||||
driver: local
|
||||
app_storage:
|
||||
driver: local
|
||||
nginx_logs:
|
||||
driver: local
|
||||
|
||||
networks:
|
||||
course-plat-network:
|
||||
driver: bridge
|
||||
@@ -0,0 +1,131 @@
|
||||
# Nginx Reverse Proxy para Course Plat
|
||||
# Uso: Copiar para docker/production/nginx.conf ou /etc/nginx/sites-available/course-plat
|
||||
|
||||
events {
|
||||
worker_connections 1024;
|
||||
}
|
||||
|
||||
http {
|
||||
include /etc/nginx/mime.types;
|
||||
default_type application/octet-stream;
|
||||
|
||||
# Logging
|
||||
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
|
||||
'$status $body_bytes_sent "$http_referer" '
|
||||
'"$http_user_agent" "$http_x_forwarded_for"';
|
||||
|
||||
access_log /var/log/nginx/access.log main;
|
||||
error_log /var/log/nginx/error.log warn;
|
||||
|
||||
# Performance
|
||||
sendfile on;
|
||||
tcp_nopush on;
|
||||
tcp_nodelay on;
|
||||
keepalive_timeout 65;
|
||||
types_hash_max_size 2048;
|
||||
|
||||
# Gzip compression
|
||||
gzip on;
|
||||
gzip_vary on;
|
||||
gzip_proxied any;
|
||||
gzip_comp_level 6;
|
||||
gzip_types text/plain text/css text/xml text/javascript
|
||||
application/json application/javascript application/xml+rss
|
||||
application/rss+xml font/truetype font/opentype
|
||||
application/vnd.ms-fontobject image/svg+xml;
|
||||
|
||||
# Rate limiting
|
||||
limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;
|
||||
limit_req_zone $binary_remote_addr zone=login:10m rate=5r/m;
|
||||
|
||||
# Upstream da aplicação
|
||||
upstream app_backend {
|
||||
server app:3000;
|
||||
keepalive 32;
|
||||
}
|
||||
|
||||
# HTTP -> HTTPS redirect (descomentar em prod com SSL)
|
||||
# server {
|
||||
# listen 80;
|
||||
# server_name seu-dominio.com.br;
|
||||
# return 301 https://$server_name$request_uri;
|
||||
# }
|
||||
|
||||
# Server principal
|
||||
server {
|
||||
listen 80;
|
||||
# listen 443 ssl http2; # Descomentar para SSL
|
||||
server_name _;
|
||||
|
||||
# SSL Configuration (descomentar para production)
|
||||
# ssl_certificate /etc/nginx/ssl/cert.pem;
|
||||
# ssl_certificate_key /etc/nginx/ssl/key.pem;
|
||||
# ssl_protocols TLSv1.2 TLSv1.3;
|
||||
# ssl_ciphers HIGH:!aNULL:!MD5;
|
||||
|
||||
# Security headers
|
||||
add_header X-Frame-Options "SAMEORIGIN" always;
|
||||
add_header X-Content-Type-Options "nosniff" always;
|
||||
add_header X-XSS-Protection "1; mode=block" always;
|
||||
add_header Referrer-Policy "no-referrer-when-downgrade" always;
|
||||
|
||||
# Client body size limit (uploads)
|
||||
client_max_body_size 500M;
|
||||
|
||||
# Proxy settings
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection 'upgrade';
|
||||
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;
|
||||
proxy_cache_bypass $http_upgrade;
|
||||
|
||||
# Next.js static files (com cache)
|
||||
location /_next/static {
|
||||
alias /var/www/.next/static;
|
||||
expires 365d;
|
||||
add_header Cache-Control "public, immutable";
|
||||
}
|
||||
|
||||
location /static {
|
||||
alias /var/www/public;
|
||||
expires 30d;
|
||||
add_header Cache-Control "public";
|
||||
}
|
||||
|
||||
# API routes (sem cache, com rate limit)
|
||||
location /api/ {
|
||||
limit_req zone=api burst=20 nodelay;
|
||||
proxy_pass http://app_backend;
|
||||
}
|
||||
|
||||
# Auth endpoints (rate limit mais estrito)
|
||||
location /api/auth/ {
|
||||
limit_req zone=login burst=5 nodelay;
|
||||
proxy_pass http://app_backend;
|
||||
}
|
||||
|
||||
# File downloads (proxy)
|
||||
location /api/files/ {
|
||||
proxy_pass http://app_backend;
|
||||
proxy_buffering on;
|
||||
proxy_buffer_size 4k;
|
||||
proxy_buffers 8 4k;
|
||||
}
|
||||
|
||||
# Todas as outras requests
|
||||
location / {
|
||||
proxy_pass http://app_backend;
|
||||
proxy_read_timeout 300s;
|
||||
proxy_connect_timeout 75s;
|
||||
}
|
||||
|
||||
# Health check
|
||||
location /health {
|
||||
access_log off;
|
||||
proxy_pass http://app_backend/api/health;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user