feat: add deployment configuration and CI/CD pipeline

Deployment infrastructure:

- Docker:
  - Dockerfile.api: Multi-stage build for API
  - Dockerfile.web: Multi-stage build for Next.js
  - docker-compose.yml: Development orchestration
  - docker-compose.prod.yml: Production overrides
  - nginx/nginx.conf: Reverse proxy with SSL

- Platform configs:
  - vercel.json: Vercel deployment settings
  - railway.json: Railway deployment settings
  - nixpacks.toml: Nixpacks build config

- CI/CD:
  - .github/workflows/deploy.yml: Full pipeline
    - Lint and type check
    - Unit tests
    - Build verification
    - Auto-deploy to Vercel (frontend)
    - Auto-deploy to Railway (backend)
    - Preview deployments for PRs

- Scripts:
  - scripts/deploy.sh: Manual deploy helper

- Documentation:
  - docs/deployment-guide.md: Complete guide covering
    Vercel+Railway, Docker, Kubernetes options

- Environment:
  - .env.production.example: All production vars documented
This commit is contained in:
Claude 2026-01-11 00:12:32 +00:00
parent b444974107
commit 8e6d8fba70
No known key found for this signature in database
13 changed files with 1676 additions and 0 deletions

232
.github/workflows/deploy.yml vendored Normal file
View File

@ -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 != ''

View File

@ -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

71
bmad-web/Dockerfile.api Normal file
View File

@ -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"]

61
bmad-web/Dockerfile.web Normal file
View File

@ -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"]

View File

@ -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"

View File

@ -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
}
}

View File

@ -2,11 +2,64 @@
const nextConfig = { const nextConfig = {
reactStrictMode: true, reactStrictMode: true,
transpilePackages: ['@bmad/core', '@bmad/ui'], transpilePackages: ['@bmad/core', '@bmad/ui'],
// Enable standalone output for Docker
output: 'standalone',
experimental: { experimental: {
serverComponentsExternalPackages: ['@bmad/core'], serverComponentsExternalPackages: ['@bmad/core'],
}, },
images: { images: {
domains: ['avatars.githubusercontent.com'], 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*`,
},
]
: [];
}, },
}; };

View File

@ -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*"
}
]
}

View File

@ -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:

107
bmad-web/docker-compose.yml Normal file
View File

@ -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

View File

@ -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** |

150
bmad-web/nginx/nginx.conf Normal file
View File

@ -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;
}
}
}

162
bmad-web/scripts/deploy.sh Executable file
View File

@ -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