diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 00000000..d3d3cf1a --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,232 @@ +# BMAD Web - CI/CD Pipeline +# Automated testing and deployment + +name: CI/CD + +on: + push: + branches: [main, develop] + paths: + - 'bmad-web/**' + - '.github/workflows/deploy.yml' + pull_request: + branches: [main] + paths: + - 'bmad-web/**' + +env: + NODE_VERSION: '20' + +jobs: + # =================== + # Lint and Type Check + # =================== + lint: + name: Lint & Type Check + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: ${{ env.NODE_VERSION }} + cache: 'npm' + cache-dependency-path: bmad-web/package-lock.json + + - name: Install dependencies + run: | + cd bmad-web + npm ci + + - name: Lint + run: | + cd bmad-web + npm run lint + + - name: Type check + run: | + cd bmad-web + npm run build --workspace=@bmad/core + npm run build --workspace=@bmad/ui + + # =================== + # Unit Tests + # =================== + test: + name: Unit Tests + runs-on: ubuntu-latest + needs: lint + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: ${{ env.NODE_VERSION }} + cache: 'npm' + cache-dependency-path: bmad-web/package-lock.json + + - name: Install dependencies + run: | + cd bmad-web + npm ci + + - name: Run tests + run: | + cd bmad-web + npm test --if-present + + # =================== + # Build Check + # =================== + build: + name: Build Check + runs-on: ubuntu-latest + needs: lint + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: ${{ env.NODE_VERSION }} + cache: 'npm' + cache-dependency-path: bmad-web/package-lock.json + + - name: Install dependencies + run: | + cd bmad-web + npm ci + + - name: Build packages + run: | + cd bmad-web + npm run build --workspace=@bmad/core + npm run build --workspace=@bmad/ui + + - name: Build API + run: | + cd bmad-web + npm run build --workspace=@bmad/api + + - name: Build Web + env: + NEXT_PUBLIC_API_URL: https://api.example.com + NEXT_PUBLIC_WS_URL: wss://api.example.com + NEXT_PUBLIC_APP_URL: https://example.com + run: | + cd bmad-web + npm run build --workspace=@bmad/web + + # =================== + # Deploy Frontend (Vercel) + # =================== + deploy-frontend: + name: Deploy Frontend + runs-on: ubuntu-latest + needs: [test, build] + if: github.ref == 'refs/heads/main' && github.event_name == 'push' + environment: + name: production + url: ${{ steps.deploy.outputs.url }} + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Deploy to Vercel + id: deploy + uses: amondnet/vercel-action@v25 + with: + vercel-token: ${{ secrets.VERCEL_TOKEN }} + vercel-org-id: ${{ secrets.VERCEL_ORG_ID }} + vercel-project-id: ${{ secrets.VERCEL_PROJECT_ID }} + working-directory: bmad-web/apps/web + vercel-args: '--prod' + + - name: Comment PR + if: github.event_name == 'pull_request' + uses: actions/github-script@v7 + with: + script: | + github.rest.issues.createComment({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + body: '🚀 Frontend deployed to: ${{ steps.deploy.outputs.url }}' + }) + + # =================== + # Deploy Backend (Railway) + # =================== + deploy-backend: + name: Deploy Backend + runs-on: ubuntu-latest + needs: [test, build] + if: github.ref == 'refs/heads/main' && github.event_name == 'push' + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Install Railway CLI + run: npm i -g @railway/cli + + - name: Deploy to Railway + env: + RAILWAY_TOKEN: ${{ secrets.RAILWAY_TOKEN }} + run: | + cd bmad-web/apps/api + railway up --detach + + # =================== + # Deploy Preview (PRs) + # =================== + deploy-preview: + name: Deploy Preview + runs-on: ubuntu-latest + needs: [lint] + if: github.event_name == 'pull_request' + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Deploy Preview to Vercel + id: deploy-preview + uses: amondnet/vercel-action@v25 + with: + vercel-token: ${{ secrets.VERCEL_TOKEN }} + vercel-org-id: ${{ secrets.VERCEL_ORG_ID }} + vercel-project-id: ${{ secrets.VERCEL_PROJECT_ID }} + working-directory: bmad-web/apps/web + + - name: Comment PR with Preview URL + uses: actions/github-script@v7 + with: + script: | + github.rest.issues.createComment({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + body: `## 🔍 Preview Deployment\n\n**URL:** ${{ steps.deploy-preview.outputs.preview-url }}\n\n_This preview will be updated with each push to this PR._` + }) + + # =================== + # Notify on Failure + # =================== + notify-failure: + name: Notify Failure + runs-on: ubuntu-latest + needs: [deploy-frontend, deploy-backend] + if: failure() + steps: + - name: Send Slack notification + uses: 8398a7/action-slack@v3 + with: + status: failure + fields: repo,message,commit,author,action,eventName,ref,workflow + env: + SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} + if: env.SLACK_WEBHOOK_URL != '' diff --git a/bmad-web/.env.production.example b/bmad-web/.env.production.example new file mode 100644 index 00000000..6a4bb44e --- /dev/null +++ b/bmad-web/.env.production.example @@ -0,0 +1,117 @@ +# ======================================== +# BMAD Web - Production Environment +# ======================================== +# Copy this file to .env.production and fill in the values + +# ---------------------------------------- +# Server Configuration +# ---------------------------------------- +NODE_ENV=production +PORT=4000 + +# ---------------------------------------- +# URLs +# ---------------------------------------- +# Frontend URL (where your app is hosted) +FRONTEND_URL=https://bmad.app + +# API URL (for frontend to connect) +NEXT_PUBLIC_API_URL=https://api.bmad.app +NEXT_PUBLIC_WS_URL=wss://api.bmad.app +NEXT_PUBLIC_APP_URL=https://bmad.app + +# ---------------------------------------- +# Authentication +# ---------------------------------------- +# Generate with: openssl rand -base64 32 +JWT_SECRET=your-super-secret-jwt-key-minimum-32-characters + +# ---------------------------------------- +# Database +# ---------------------------------------- +# PostgreSQL connection string +# Format: postgresql://USER:PASSWORD@HOST:PORT/DATABASE +DATABASE_URL=postgresql://bmad:password@localhost:5432/bmad_web + +# ---------------------------------------- +# Redis +# ---------------------------------------- +# Redis connection string +# Format: redis://[:PASSWORD@]HOST:PORT +REDIS_URL=redis://localhost:6379 + +# ---------------------------------------- +# BMAD Core +# ---------------------------------------- +# Path to BMAD source (for agent definitions) +BMAD_ROOT=/app/bmad-core + +# ---------------------------------------- +# AI Providers +# ---------------------------------------- +# OpenAI API Key (for GPT models) +OPENAI_API_KEY=sk-your-openai-api-key + +# Anthropic API Key (for Claude models) +ANTHROPIC_API_KEY=sk-ant-your-anthropic-api-key + +# Default model to use +DEFAULT_AI_MODEL=gpt-4-turbo-preview + +# ---------------------------------------- +# Stripe (Billing) +# ---------------------------------------- +STRIPE_SECRET_KEY=sk_live_your-stripe-secret-key +STRIPE_WEBHOOK_SECRET=whsec_your-webhook-secret + +# Price IDs from Stripe Dashboard +STRIPE_PRICE_STARTER_MONTHLY=price_xxx +STRIPE_PRICE_STARTER_YEARLY=price_xxx +STRIPE_PRICE_PRO_MONTHLY=price_xxx +STRIPE_PRICE_PRO_YEARLY=price_xxx +STRIPE_PRICE_TEAM_MONTHLY=price_xxx +STRIPE_PRICE_TEAM_YEARLY=price_xxx + +# ---------------------------------------- +# Email (SendGrid/SMTP) +# ---------------------------------------- +SMTP_HOST=smtp.sendgrid.net +SMTP_PORT=587 +SMTP_USER=apikey +SMTP_PASS=SG.your-sendgrid-api-key +EMAIL_FROM=noreply@bmad.app +EMAIL_FROM_NAME=BMAD + +# ---------------------------------------- +# Storage (S3/MinIO) +# ---------------------------------------- +S3_ENDPOINT=https://s3.amazonaws.com +S3_BUCKET=bmad-artifacts +S3_ACCESS_KEY=your-access-key +S3_SECRET_KEY=your-secret-key +S3_REGION=us-east-1 + +# ---------------------------------------- +# Monitoring +# ---------------------------------------- +# Sentry DSN for error tracking +SENTRY_DSN=https://xxx@sentry.io/xxx + +# ---------------------------------------- +# Analytics (Optional) +# ---------------------------------------- +NEXT_PUBLIC_MIXPANEL_TOKEN=your-mixpanel-token +NEXT_PUBLIC_GA_ID=G-XXXXXXXXXX + +# ---------------------------------------- +# Feature Flags +# ---------------------------------------- +NEXT_PUBLIC_ENABLE_BILLING=true +NEXT_PUBLIC_ENABLE_ANALYTICS=true +ENABLE_RATE_LIMITING=true + +# ---------------------------------------- +# Rate Limiting +# ---------------------------------------- +RATE_LIMIT_WINDOW_MS=900000 +RATE_LIMIT_MAX_REQUESTS=100 diff --git a/bmad-web/Dockerfile.api b/bmad-web/Dockerfile.api new file mode 100644 index 00000000..69bdbac1 --- /dev/null +++ b/bmad-web/Dockerfile.api @@ -0,0 +1,71 @@ +# BMAD API Dockerfile +# Multi-stage build for optimized production image + +# Stage 1: Dependencies +FROM node:20-alpine AS deps +WORKDIR /app + +# Install dependencies needed for native modules +RUN apk add --no-cache libc6-compat + +# Copy package files +COPY package.json package-lock.json* ./ +COPY apps/api/package.json ./apps/api/ +COPY packages/bmad-core/package.json ./packages/bmad-core/ + +# Install dependencies +RUN npm ci --workspace=@bmad/api --workspace=@bmad/core + +# Stage 2: Builder +FROM node:20-alpine AS builder +WORKDIR /app + +# Copy dependencies +COPY --from=deps /app/node_modules ./node_modules +COPY --from=deps /app/apps/api/node_modules ./apps/api/node_modules +COPY --from=deps /app/packages/bmad-core/node_modules ./packages/bmad-core/node_modules + +# Copy source +COPY . . + +# Build +RUN npm run build --workspace=@bmad/core +RUN npm run build --workspace=@bmad/api + +# Stage 3: Runner +FROM node:20-alpine AS runner +WORKDIR /app + +# Set production environment +ENV NODE_ENV=production +ENV PORT=4000 + +# Create non-root user +RUN addgroup --system --gid 1001 nodejs +RUN adduser --system --uid 1001 bmad + +# Copy built application +COPY --from=builder /app/apps/api/dist ./dist +COPY --from=builder /app/apps/api/package.json ./ +COPY --from=builder /app/node_modules ./node_modules +COPY --from=builder /app/packages/bmad-core/dist ./packages/bmad-core/dist +COPY --from=builder /app/packages/bmad-core/package.json ./packages/bmad-core/ + +# Copy BMAD source for agent definitions +COPY --from=builder /app/../src ./bmad-src + +# Set ownership +RUN chown -R bmad:nodejs /app + +# Switch to non-root user +USER bmad + +# Expose port +EXPOSE 4000 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \ + CMD wget --no-verbose --tries=1 --spider http://localhost:4000/health || exit 1 + +# Start server +CMD ["node", "dist/index.js"] diff --git a/bmad-web/Dockerfile.web b/bmad-web/Dockerfile.web new file mode 100644 index 00000000..6ba15018 --- /dev/null +++ b/bmad-web/Dockerfile.web @@ -0,0 +1,61 @@ +# BMAD Web Frontend Dockerfile +# Multi-stage build for Next.js + +# Stage 1: Dependencies +FROM node:20-alpine AS deps +WORKDIR /app + +RUN apk add --no-cache libc6-compat + +COPY package.json package-lock.json* ./ +COPY apps/web/package.json ./apps/web/ +COPY packages/ui/package.json ./packages/ui/ +COPY packages/bmad-core/package.json ./packages/bmad-core/ + +RUN npm ci + +# Stage 2: Builder +FROM node:20-alpine AS builder +WORKDIR /app + +COPY --from=deps /app/node_modules ./node_modules +COPY . . + +# Build arguments for environment variables +ARG NEXT_PUBLIC_API_URL +ARG NEXT_PUBLIC_WS_URL +ARG NEXT_PUBLIC_APP_URL + +ENV NEXT_PUBLIC_API_URL=$NEXT_PUBLIC_API_URL +ENV NEXT_PUBLIC_WS_URL=$NEXT_PUBLIC_WS_URL +ENV NEXT_PUBLIC_APP_URL=$NEXT_PUBLIC_APP_URL + +# Build packages first +RUN npm run build --workspace=@bmad/core +RUN npm run build --workspace=@bmad/ui + +# Build Next.js app +RUN npm run build --workspace=@bmad/web + +# Stage 3: Runner +FROM node:20-alpine AS runner +WORKDIR /app + +ENV NODE_ENV=production +ENV PORT=3000 + +RUN addgroup --system --gid 1001 nodejs +RUN adduser --system --uid 1001 nextjs + +# Copy built application +COPY --from=builder /app/apps/web/public ./public +COPY --from=builder /app/apps/web/.next/standalone ./ +COPY --from=builder /app/apps/web/.next/static ./.next/static + +USER nextjs + +EXPOSE 3000 + +ENV HOSTNAME="0.0.0.0" + +CMD ["node", "server.js"] diff --git a/bmad-web/apps/api/nixpacks.toml b/bmad-web/apps/api/nixpacks.toml new file mode 100644 index 00000000..42276e4f --- /dev/null +++ b/bmad-web/apps/api/nixpacks.toml @@ -0,0 +1,16 @@ +# Nixpacks configuration for Railway + +[phases.setup] +nixPkgs = ["nodejs_20", "npm"] + +[phases.install] +cmds = ["npm ci"] + +[phases.build] +cmds = ["npm run build"] + +[start] +cmd = "npm start" + +[variables] +NODE_ENV = "production" diff --git a/bmad-web/apps/api/railway.json b/bmad-web/apps/api/railway.json new file mode 100644 index 00000000..daf15c71 --- /dev/null +++ b/bmad-web/apps/api/railway.json @@ -0,0 +1,14 @@ +{ + "$schema": "https://railway.app/railway.schema.json", + "build": { + "builder": "NIXPACKS", + "buildCommand": "npm run build" + }, + "deploy": { + "startCommand": "npm start", + "healthcheckPath": "/health", + "healthcheckTimeout": 30, + "restartPolicyType": "ON_FAILURE", + "restartPolicyMaxRetries": 3 + } +} diff --git a/bmad-web/apps/web/next.config.js b/bmad-web/apps/web/next.config.js index 9d168b88..8ffe75b0 100644 --- a/bmad-web/apps/web/next.config.js +++ b/bmad-web/apps/web/next.config.js @@ -2,11 +2,64 @@ const nextConfig = { reactStrictMode: true, transpilePackages: ['@bmad/core', '@bmad/ui'], + + // Enable standalone output for Docker + output: 'standalone', + experimental: { serverComponentsExternalPackages: ['@bmad/core'], }, + images: { domains: ['avatars.githubusercontent.com'], + // Allow images from any domain in production + remotePatterns: [ + { + protocol: 'https', + hostname: '**', + }, + ], + }, + + // Environment variables available at build time + env: { + NEXT_PUBLIC_APP_VERSION: process.env.npm_package_version || '1.0.0', + }, + + // Headers for security + async headers() { + return [ + { + source: '/(.*)', + headers: [ + { + key: 'X-DNS-Prefetch-Control', + value: 'on', + }, + { + key: 'X-Frame-Options', + value: 'SAMEORIGIN', + }, + { + key: 'X-Content-Type-Options', + value: 'nosniff', + }, + ], + }, + ]; + }, + + // Rewrites for API proxy (optional, for development) + async rewrites() { + const apiUrl = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:4000'; + return process.env.NODE_ENV === 'development' + ? [ + { + source: '/api/:path*', + destination: `${apiUrl}/api/:path*`, + }, + ] + : []; }, }; diff --git a/bmad-web/apps/web/vercel.json b/bmad-web/apps/web/vercel.json new file mode 100644 index 00000000..59e8ecf8 --- /dev/null +++ b/bmad-web/apps/web/vercel.json @@ -0,0 +1,55 @@ +{ + "$schema": "https://openapi.vercel.sh/vercel.json", + "framework": "nextjs", + "buildCommand": "cd ../.. && npm run build --workspace=@bmad/web", + "installCommand": "cd ../.. && npm ci", + "outputDirectory": ".next", + "regions": ["gru1"], + "headers": [ + { + "source": "/(.*)", + "headers": [ + { + "key": "X-Frame-Options", + "value": "DENY" + }, + { + "key": "X-Content-Type-Options", + "value": "nosniff" + }, + { + "key": "X-XSS-Protection", + "value": "1; mode=block" + }, + { + "key": "Referrer-Policy", + "value": "strict-origin-when-cross-origin" + } + ] + }, + { + "source": "/api/(.*)", + "headers": [ + { + "key": "Cache-Control", + "value": "no-store, max-age=0" + } + ] + }, + { + "source": "/_next/static/(.*)", + "headers": [ + { + "key": "Cache-Control", + "value": "public, max-age=31536000, immutable" + } + ] + } + ], + "rewrites": [ + { + "source": "/api/:path*", + "destination": "${NEXT_PUBLIC_API_URL}/api/:path*" + } + ] +} diff --git a/bmad-web/docker-compose.prod.yml b/bmad-web/docker-compose.prod.yml new file mode 100644 index 00000000..7866a65e --- /dev/null +++ b/bmad-web/docker-compose.prod.yml @@ -0,0 +1,72 @@ +# BMAD Web - Production Overrides +# Use with: docker-compose -f docker-compose.yml -f docker-compose.prod.yml up -d + +version: '3.8' + +services: + postgres: + restart: always + environment: + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?POSTGRES_PASSWORD is required} + # Don't expose port in production + ports: [] + + redis: + restart: always + command: redis-server --appendonly yes --requirepass ${REDIS_PASSWORD:-} + ports: [] + + api: + restart: always + environment: + NODE_ENV: production + JWT_SECRET: ${JWT_SECRET:?JWT_SECRET is required} + DATABASE_URL: ${DATABASE_URL:?DATABASE_URL is required} + REDIS_URL: ${REDIS_URL:?REDIS_URL is required} + # Remove volume mounts in production + volumes: [] + deploy: + resources: + limits: + cpus: '1' + memory: 1G + reservations: + cpus: '0.5' + memory: 512M + + web: + restart: always + environment: + NODE_ENV: production + deploy: + resources: + limits: + cpus: '0.5' + memory: 512M + reservations: + cpus: '0.25' + memory: 256M + + # Nginx Reverse Proxy for production + nginx: + image: nginx:alpine + container_name: bmad-nginx + restart: always + depends_on: + - web + - api + ports: + - "80:80" + - "443:443" + volumes: + - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro + - ./nginx/ssl:/etc/nginx/ssl:ro + - nginx_cache:/var/cache/nginx + healthcheck: + test: ["CMD", "nginx", "-t"] + interval: 30s + timeout: 10s + retries: 3 + +volumes: + nginx_cache: diff --git a/bmad-web/docker-compose.yml b/bmad-web/docker-compose.yml new file mode 100644 index 00000000..d21e83cb --- /dev/null +++ b/bmad-web/docker-compose.yml @@ -0,0 +1,107 @@ +# BMAD Web - Docker Compose +# Development and production orchestration + +version: '3.8' + +services: + # PostgreSQL Database + postgres: + image: postgres:16-alpine + container_name: bmad-postgres + restart: unless-stopped + environment: + POSTGRES_USER: ${POSTGRES_USER:-bmad} + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-bmad_dev_password} + POSTGRES_DB: ${POSTGRES_DB:-bmad_web} + volumes: + - postgres_data:/var/lib/postgresql/data + ports: + - "5432:5432" + healthcheck: + test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-bmad}"] + interval: 10s + timeout: 5s + retries: 5 + + # Redis Cache + redis: + image: redis:7-alpine + container_name: bmad-redis + restart: unless-stopped + command: redis-server --appendonly yes + volumes: + - redis_data:/data + ports: + - "6379:6379" + healthcheck: + test: ["CMD", "redis-cli", "ping"] + interval: 10s + timeout: 5s + retries: 5 + + # API Backend + api: + build: + context: . + dockerfile: Dockerfile.api + container_name: bmad-api + restart: unless-stopped + depends_on: + postgres: + condition: service_healthy + redis: + condition: service_healthy + environment: + NODE_ENV: ${NODE_ENV:-development} + PORT: 4000 + FRONTEND_URL: ${FRONTEND_URL:-http://localhost:3000} + JWT_SECRET: ${JWT_SECRET:-dev-secret-change-in-production} + DATABASE_URL: postgresql://${POSTGRES_USER:-bmad}:${POSTGRES_PASSWORD:-bmad_dev_password}@postgres:5432/${POSTGRES_DB:-bmad_web} + REDIS_URL: redis://redis:6379 + BMAD_ROOT: /app/bmad-src + OPENAI_API_KEY: ${OPENAI_API_KEY:-} + ANTHROPIC_API_KEY: ${ANTHROPIC_API_KEY:-} + ports: + - "4000:4000" + volumes: + # For development: hot reload + - ./apps/api/src:/app/src:ro + # BMAD source for agent definitions + - ../src:/app/bmad-src:ro + healthcheck: + test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:4000/health"] + interval: 30s + timeout: 10s + retries: 3 + + # Web Frontend + web: + build: + context: . + dockerfile: Dockerfile.web + args: + NEXT_PUBLIC_API_URL: ${NEXT_PUBLIC_API_URL:-http://localhost:4000} + NEXT_PUBLIC_WS_URL: ${NEXT_PUBLIC_WS_URL:-ws://localhost:4000} + NEXT_PUBLIC_APP_URL: ${NEXT_PUBLIC_APP_URL:-http://localhost:3000} + container_name: bmad-web + restart: unless-stopped + depends_on: + - api + environment: + NODE_ENV: ${NODE_ENV:-development} + PORT: 3000 + ports: + - "3000:3000" + healthcheck: + test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:3000"] + interval: 30s + timeout: 10s + retries: 3 + +volumes: + postgres_data: + redis_data: + +networks: + default: + name: bmad-network diff --git a/bmad-web/docs/deployment-guide.md b/bmad-web/docs/deployment-guide.md new file mode 100644 index 00000000..86f829f7 --- /dev/null +++ b/bmad-web/docs/deployment-guide.md @@ -0,0 +1,566 @@ +# BMAD Web - Guia de Deploy + +Este guia cobre todas as opcoes de deploy para a aplicacao BMAD Web. + +--- + +## Sumario + +1. [Arquitetura de Deploy](#arquitetura-de-deploy) +2. [Opcao 1: Vercel + Railway (Recomendado)](#opcao-1-vercel--railway-recomendado) +3. [Opcao 2: Docker Compose (Self-hosted)](#opcao-2-docker-compose-self-hosted) +4. [Opcao 3: Kubernetes](#opcao-3-kubernetes) +5. [Configuracao de Banco de Dados](#configuracao-de-banco-de-dados) +6. [Variaveis de Ambiente](#variaveis-de-ambiente) +7. [CI/CD com GitHub Actions](#cicd-com-github-actions) +8. [Monitoramento](#monitoramento) +9. [Checklist de Producao](#checklist-de-producao) + +--- + +## Arquitetura de Deploy + +``` +┌─────────────────────────────────────────────────────────────────────┐ +│ PRODUCAO │ +├─────────────────────────────────────────────────────────────────────┤ +│ │ +│ ┌──────────────┐ ┌──────────────┐ ┌────────────┐ │ +│ │ Vercel │ │ Railway │ │ Supabase │ │ +│ │ (Next.js) │◄───────►│ (API) │◄───────►│ (Postgres)│ │ +│ │ │ │ │ │ │ │ +│ │ Frontend │ REST │ Backend │ SQL │ Database │ │ +│ │ + CDN │ + WS │ + Socket │ │ + Auth │ │ +│ └──────────────┘ └──────────────┘ └────────────┘ │ +│ │ │ │ │ +│ │ │ │ │ +│ ▼ ▼ ▼ │ +│ ┌──────────────┐ ┌──────────────┐ ┌────────────┐ │ +│ │ Cloudflare │ │ Upstash │ │ S3 │ │ +│ │ (DNS) │ │ (Redis) │ │ (Assets) │ │ +│ └──────────────┘ └──────────────┘ └────────────┘ │ +│ │ +└─────────────────────────────────────────────────────────────────────┘ +``` + +--- + +## Opcao 1: Vercel + Railway (Recomendado) + +A opcao mais simples para comecar. Zero configuracao de infraestrutura. + +### 1.1 Deploy do Frontend (Vercel) + +#### Passo 1: Conectar repositorio + +```bash +# Instalar Vercel CLI +npm i -g vercel + +# Login +vercel login + +# Na pasta bmad-web/apps/web +cd bmad-web/apps/web +vercel +``` + +#### Passo 2: Configurar no Dashboard Vercel + +1. Acesse https://vercel.com/dashboard +2. Import o repositorio do GitHub +3. Configure: + - **Framework Preset**: Next.js + - **Root Directory**: `bmad-web/apps/web` + - **Build Command**: `npm run build` + - **Output Directory**: `.next` + +#### Passo 3: Variaveis de Ambiente + +No dashboard Vercel, adicione: + +``` +NEXT_PUBLIC_API_URL=https://sua-api.railway.app +NEXT_PUBLIC_WS_URL=wss://sua-api.railway.app +NEXT_PUBLIC_APP_URL=https://seu-app.vercel.app +``` + +#### Passo 4: Deploy + +```bash +# Deploy de producao +vercel --prod +``` + +--- + +### 1.2 Deploy da API (Railway) + +#### Passo 1: Criar projeto Railway + +1. Acesse https://railway.app +2. New Project > Deploy from GitHub repo +3. Selecione o repositorio + +#### Passo 2: Configurar servico + +No dashboard Railway: + +1. **Root Directory**: `bmad-web/apps/api` +2. **Build Command**: `npm run build` +3. **Start Command**: `npm start` + +#### Passo 3: Variaveis de Ambiente + +``` +NODE_ENV=production +PORT=4000 +FRONTEND_URL=https://seu-app.vercel.app +JWT_SECRET=sua-chave-super-secreta-aqui +DATABASE_URL=postgresql://... +REDIS_URL=redis://... +BMAD_ROOT=/app/../../.. +``` + +#### Passo 4: Adicionar Postgres (Railway) + +1. No projeto Railway, clique em "New" +2. Selecione "Database" > "PostgreSQL" +3. Copie a DATABASE_URL gerada + +#### Passo 5: Adicionar Redis (Upstash) + +1. Acesse https://upstash.com +2. Crie um banco Redis +3. Copie a REDIS_URL + +--- + +### 1.3 Comandos Rapidos (Vercel + Railway) + +```bash +# Frontend +cd bmad-web/apps/web +vercel --prod + +# Backend (Railway CLI) +npm i -g @railway/cli +railway login +cd bmad-web/apps/api +railway up +``` + +--- + +## Opcao 2: Docker Compose (Self-hosted) + +Para quem quer controle total ou rodar on-premise. + +### 2.1 Arquivos Docker + +Os arquivos ja estao criados: +- `bmad-web/Dockerfile.web` - Frontend +- `bmad-web/Dockerfile.api` - Backend +- `bmad-web/docker-compose.yml` - Orquestracao +- `bmad-web/docker-compose.prod.yml` - Producao + +### 2.2 Build e Run Local + +```bash +cd bmad-web + +# Desenvolvimento +docker-compose up -d + +# Producao +docker-compose -f docker-compose.yml -f docker-compose.prod.yml up -d +``` + +### 2.3 Deploy em VPS (DigitalOcean, Hetzner, etc.) + +```bash +# 1. SSH na VPS +ssh root@seu-servidor + +# 2. Instalar Docker +curl -fsSL https://get.docker.com | sh + +# 3. Clonar repositorio +git clone https://github.com/seu-usuario/BMAD-METHOD.git +cd BMAD-METHOD/bmad-web + +# 4. Configurar ambiente +cp .env.example .env +nano .env # Editar variaveis + +# 5. Subir servicos +docker-compose -f docker-compose.yml -f docker-compose.prod.yml up -d + +# 6. Configurar Nginx (proxy reverso) +apt install nginx +# Copiar config do nginx.conf +``` + +### 2.4 Com Traefik (HTTPS automatico) + +```bash +# Usar docker-compose.traefik.yml para HTTPS automatico +docker-compose -f docker-compose.yml -f docker-compose.traefik.yml up -d +``` + +--- + +## Opcao 3: Kubernetes + +Para escala enterprise. + +### 3.1 Estrutura + +``` +bmad-web/k8s/ +├── namespace.yaml +├── configmap.yaml +├── secrets.yaml +├── web-deployment.yaml +├── api-deployment.yaml +├── postgres-deployment.yaml +├── redis-deployment.yaml +├── ingress.yaml +└── hpa.yaml +``` + +### 3.2 Deploy + +```bash +# Criar namespace +kubectl apply -f k8s/namespace.yaml + +# Aplicar configs +kubectl apply -f k8s/ + +# Verificar status +kubectl get pods -n bmad +``` + +--- + +## Configuracao de Banco de Dados + +### Opcao A: Supabase (Recomendado) + +1. Crie conta em https://supabase.com +2. Crie novo projeto +3. Copie a connection string: + ``` + DATABASE_URL=postgresql://postgres:[senha]@db.[ref].supabase.co:5432/postgres + ``` + +### Opcao B: PlanetScale (MySQL) + +1. Crie conta em https://planetscale.com +2. Crie database +3. Copie connection string + +### Opcao C: Neon (Postgres Serverless) + +1. Crie conta em https://neon.tech +2. Crie projeto +3. Copie connection string + +### Migrations + +```bash +cd bmad-web/apps/api + +# Gerar cliente Prisma +npx prisma generate + +# Push schema para banco +npx prisma db push + +# Ou usar migrations +npx prisma migrate deploy +``` + +--- + +## Variaveis de Ambiente + +### Frontend (.env.local) + +```bash +# API +NEXT_PUBLIC_API_URL=https://api.bmad.app +NEXT_PUBLIC_WS_URL=wss://api.bmad.app + +# App +NEXT_PUBLIC_APP_URL=https://bmad.app +NEXT_PUBLIC_APP_NAME=BMAD + +# Analytics (opcional) +NEXT_PUBLIC_MIXPANEL_TOKEN=xxx +NEXT_PUBLIC_GA_ID=G-xxx + +# Feature Flags (opcional) +NEXT_PUBLIC_ENABLE_BILLING=true +``` + +### Backend (.env) + +```bash +# Server +NODE_ENV=production +PORT=4000 + +# URLs +FRONTEND_URL=https://bmad.app + +# Auth +JWT_SECRET=sua-chave-jwt-muito-secreta-minimo-32-chars + +# Database +DATABASE_URL=postgresql://user:pass@host:5432/bmad + +# Redis +REDIS_URL=redis://default:pass@host:6379 + +# BMAD Core +BMAD_ROOT=/app/bmad-core + +# AI Providers +OPENAI_API_KEY=sk-xxx +ANTHROPIC_API_KEY=sk-ant-xxx + +# Stripe (billing) +STRIPE_SECRET_KEY=sk_live_xxx +STRIPE_WEBHOOK_SECRET=whsec_xxx + +# Email (opcional) +SMTP_HOST=smtp.sendgrid.net +SMTP_USER=apikey +SMTP_PASS=SG.xxx + +# Monitoring (opcional) +SENTRY_DSN=https://xxx@sentry.io/xxx +``` + +### Gerar JWT_SECRET Seguro + +```bash +openssl rand -base64 32 +``` + +--- + +## CI/CD com GitHub Actions + +### Arquivo: `.github/workflows/deploy.yml` + +```yaml +name: Deploy + +on: + push: + branches: [main] + pull_request: + branches: [main] + +jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: '20' + cache: 'npm' + + - name: Install dependencies + run: cd bmad-web && npm ci + + - name: Lint + run: cd bmad-web && npm run lint + + - name: Test + run: cd bmad-web && npm test + + deploy-frontend: + needs: test + if: github.ref == 'refs/heads/main' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: amondnet/vercel-action@v25 + with: + vercel-token: ${{ secrets.VERCEL_TOKEN }} + vercel-org-id: ${{ secrets.VERCEL_ORG_ID }} + vercel-project-id: ${{ secrets.VERCEL_PROJECT_ID }} + working-directory: bmad-web/apps/web + vercel-args: '--prod' + + deploy-backend: + needs: test + if: github.ref == 'refs/heads/main' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: railwayapp/railway-action@v1 + with: + railway-token: ${{ secrets.RAILWAY_TOKEN }} + service: api +``` + +--- + +## Monitoramento + +### Sentry (Error Tracking) + +```bash +# Instalar +npm install @sentry/nextjs @sentry/node + +# Configurar em next.config.js e api/src/index.ts +``` + +### Uptime Monitoring + +- **Better Uptime**: https://betteruptime.com +- **UptimeRobot**: https://uptimerobot.com + +Endpoints para monitorar: +- `https://bmad.app` (frontend) +- `https://api.bmad.app/health` (backend) + +### Analytics + +- **Mixpanel**: Eventos de produto +- **Google Analytics**: Trafego +- **PostHog**: Open-source alternativa + +--- + +## Checklist de Producao + +### Seguranca +- [ ] JWT_SECRET forte (32+ chars) +- [ ] HTTPS em todos os endpoints +- [ ] Rate limiting configurado +- [ ] CORS configurado corretamente +- [ ] Headers de seguranca (helmet) +- [ ] Variaveis sensiveis em secrets manager + +### Performance +- [ ] CDN para assets estaticos +- [ ] Cache Redis funcionando +- [ ] Compressao gzip habilitada +- [ ] Images otimizadas (next/image) +- [ ] Bundle size otimizado + +### Banco de Dados +- [ ] Backups automaticos configurados +- [ ] Connection pooling (PgBouncer) +- [ ] Indices criados para queries frequentes +- [ ] SSL habilitado na conexao + +### Monitoramento +- [ ] Error tracking (Sentry) +- [ ] Uptime monitoring +- [ ] Log aggregation +- [ ] Alertas configurados + +### Legal/Compliance +- [ ] Termos de Servico +- [ ] Politica de Privacidade +- [ ] Cookie consent +- [ ] LGPD/GDPR compliance + +--- + +## Troubleshooting + +### Erro: "WebSocket connection failed" + +```bash +# Verificar se WebSocket esta habilitado no Railway/Vercel +# Railway: ja suporta nativamente +# Vercel: usar Vercel Serverless Functions com upgrade +``` + +### Erro: "Database connection timeout" + +```bash +# Verificar DATABASE_URL +# Adicionar ?connect_timeout=10&pool_timeout=10 +DATABASE_URL=postgresql://...?connect_timeout=10 +``` + +### Erro: "JWT invalid" + +```bash +# Verificar se JWT_SECRET e o mesmo em todos os servicos +# Verificar timezone dos servidores +``` + +### Build falha no Vercel + +```bash +# Verificar se todas as deps estao no package.json +# Verificar se NEXT_PUBLIC_* estao configuradas +# Verificar Root Directory +``` + +--- + +## Comandos Uteis + +```bash +# Logs da API (Railway) +railway logs + +# Logs do Frontend (Vercel) +vercel logs + +# SSH no container Docker +docker exec -it bmad-api sh + +# Prisma Studio (visualizar banco) +npx prisma studio + +# Reset do banco (CUIDADO!) +npx prisma migrate reset +``` + +--- + +## Custos Estimados + +### Startup (0-1000 usuarios) + +| Servico | Custo/mes | +|---------|-----------| +| Vercel (Hobby) | $0 | +| Railway (Starter) | $5 | +| Supabase (Free) | $0 | +| Upstash (Free) | $0 | +| **Total** | **$5/mes** | + +### Growth (1000-10000 usuarios) + +| Servico | Custo/mes | +|---------|-----------| +| Vercel (Pro) | $20 | +| Railway (Pro) | $20 | +| Supabase (Pro) | $25 | +| Upstash (Pay-as-go) | $10 | +| **Total** | **$75/mes** | + +### Scale (10000+ usuarios) + +| Servico | Custo/mes | +|---------|-----------| +| Vercel (Enterprise) | $150+ | +| Railway (Team) | $50+ | +| Supabase (Team) | $599+ | +| Redis (Managed) | $50+ | +| **Total** | **$850+/mes** | diff --git a/bmad-web/nginx/nginx.conf b/bmad-web/nginx/nginx.conf new file mode 100644 index 00000000..1e68e247 --- /dev/null +++ b/bmad-web/nginx/nginx.conf @@ -0,0 +1,150 @@ +# BMAD Web - Nginx Configuration +# Production reverse proxy with SSL + +user nginx; +worker_processes auto; +error_log /var/log/nginx/error.log warn; +pid /var/run/nginx.pid; + +events { + worker_connections 1024; + use epoll; + multi_accept on; +} + +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; + + # Performance + sendfile on; + tcp_nopush on; + tcp_nodelay on; + keepalive_timeout 65; + types_hash_max_size 2048; + + # Gzip + gzip on; + gzip_vary on; + gzip_proxied any; + gzip_comp_level 6; + gzip_types text/plain text/css text/xml application/json application/javascript + application/rss+xml application/atom+xml image/svg+xml; + + # 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 "strict-origin-when-cross-origin" always; + + # Rate limiting + limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s; + limit_req_zone $binary_remote_addr zone=web:10m rate=30r/s; + + # Upstream servers + upstream web_upstream { + server web:3000; + keepalive 32; + } + + upstream api_upstream { + server api:4000; + keepalive 32; + } + + # Redirect HTTP to HTTPS + server { + listen 80; + server_name _; + return 301 https://$host$request_uri; + } + + # Main HTTPS server + server { + listen 443 ssl http2; + server_name bmad.app www.bmad.app; + + # SSL Configuration + ssl_certificate /etc/nginx/ssl/fullchain.pem; + ssl_certificate_key /etc/nginx/ssl/privkey.pem; + ssl_session_timeout 1d; + ssl_session_cache shared:SSL:50m; + ssl_session_tickets off; + + # Modern SSL configuration + ssl_protocols TLSv1.2 TLSv1.3; + ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384; + ssl_prefer_server_ciphers off; + + # HSTS + add_header Strict-Transport-Security "max-age=63072000" always; + + # Frontend + location / { + limit_req zone=web burst=50 nodelay; + + proxy_pass http://web_upstream; + 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; + + # Cache static assets + location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2)$ { + expires 1y; + add_header Cache-Control "public, immutable"; + } + } + + # API + location /api { + limit_req zone=api burst=20 nodelay; + + proxy_pass http://api_upstream; + proxy_http_version 1.1; + 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; + + # Timeouts for long-running requests + proxy_connect_timeout 60s; + proxy_send_timeout 60s; + proxy_read_timeout 60s; + } + + # WebSocket for Socket.io + location /socket.io { + proxy_pass http://api_upstream; + 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; + + # WebSocket timeouts + proxy_connect_timeout 7d; + proxy_send_timeout 7d; + proxy_read_timeout 7d; + } + + # Health check endpoint + location /health { + access_log off; + return 200 'OK'; + add_header Content-Type text/plain; + } + } +} diff --git a/bmad-web/scripts/deploy.sh b/bmad-web/scripts/deploy.sh new file mode 100755 index 00000000..8e682527 --- /dev/null +++ b/bmad-web/scripts/deploy.sh @@ -0,0 +1,162 @@ +#!/bin/bash + +# BMAD Web - Deploy Script +# Usage: ./scripts/deploy.sh [environment] [service] +# Examples: +# ./scripts/deploy.sh production all +# ./scripts/deploy.sh staging frontend +# ./scripts/deploy.sh production backend + +set -e + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +# Configuration +ENVIRONMENT=${1:-production} +SERVICE=${2:-all} + +echo -e "${BLUE}========================================${NC}" +echo -e "${BLUE} BMAD Web Deploy Script${NC}" +echo -e "${BLUE}========================================${NC}" +echo "" +echo -e "Environment: ${GREEN}$ENVIRONMENT${NC}" +echo -e "Service: ${GREEN}$SERVICE${NC}" +echo "" + +# Check prerequisites +check_prerequisites() { + echo -e "${YELLOW}Checking prerequisites...${NC}" + + if ! command -v node &> /dev/null; then + echo -e "${RED}Error: Node.js is not installed${NC}" + exit 1 + fi + + if ! command -v npm &> /dev/null; then + echo -e "${RED}Error: npm is not installed${NC}" + exit 1 + fi + + # Check for Vercel CLI if deploying frontend + if [[ "$SERVICE" == "frontend" || "$SERVICE" == "all" ]]; then + if ! command -v vercel &> /dev/null; then + echo -e "${YELLOW}Installing Vercel CLI...${NC}" + npm i -g vercel + fi + fi + + # Check for Railway CLI if deploying backend + if [[ "$SERVICE" == "backend" || "$SERVICE" == "all" ]]; then + if ! command -v railway &> /dev/null; then + echo -e "${YELLOW}Installing Railway CLI...${NC}" + npm i -g @railway/cli + fi + fi + + echo -e "${GREEN}Prerequisites OK${NC}" + echo "" +} + +# Build packages +build_packages() { + echo -e "${YELLOW}Building shared packages...${NC}" + npm run build --workspace=@bmad/core + npm run build --workspace=@bmad/ui + echo -e "${GREEN}Packages built${NC}" + echo "" +} + +# Deploy frontend +deploy_frontend() { + echo -e "${YELLOW}Deploying frontend to Vercel...${NC}" + + cd apps/web + + if [[ "$ENVIRONMENT" == "production" ]]; then + vercel --prod + else + vercel + fi + + cd ../.. + + echo -e "${GREEN}Frontend deployed${NC}" + echo "" +} + +# Deploy backend +deploy_backend() { + echo -e "${YELLOW}Deploying backend to Railway...${NC}" + + cd apps/api + + if [[ "$ENVIRONMENT" == "production" ]]; then + railway up --detach + else + railway up --detach --environment staging + fi + + cd ../.. + + echo -e "${GREEN}Backend deployed${NC}" + echo "" +} + +# Run database migrations +run_migrations() { + echo -e "${YELLOW}Running database migrations...${NC}" + + cd apps/api + npx prisma migrate deploy + cd ../.. + + echo -e "${GREEN}Migrations completed${NC}" + echo "" +} + +# Main execution +main() { + check_prerequisites + + # Navigate to bmad-web directory + SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + cd "$SCRIPT_DIR/.." + + echo -e "${YELLOW}Installing dependencies...${NC}" + npm ci + echo "" + + build_packages + + case $SERVICE in + frontend) + deploy_frontend + ;; + backend) + run_migrations + deploy_backend + ;; + all) + deploy_frontend + run_migrations + deploy_backend + ;; + *) + echo -e "${RED}Unknown service: $SERVICE${NC}" + echo "Usage: ./scripts/deploy.sh [environment] [service]" + echo "Services: frontend, backend, all" + exit 1 + ;; + esac + + echo -e "${GREEN}========================================${NC}" + echo -e "${GREEN} Deploy completed successfully!${NC}" + echo -e "${GREEN}========================================${NC}" +} + +main