Initial commit

This commit is contained in:
Rafael Dias Martins
2026-08-31 14:10:20 -03:00
commit 8fcb5aac66
454 changed files with 60207 additions and 0 deletions
+55
View File
@@ -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"]
+136
View File
@@ -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!"
+125
View File
@@ -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
+131
View File
@@ -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;
}
}
}