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:
parent
b444974107
commit
8e6d8fba70
|
|
@ -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 != ''
|
||||
|
|
@ -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
|
||||
|
|
@ -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"]
|
||||
|
|
@ -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"]
|
||||
|
|
@ -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"
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -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*`,
|
||||
},
|
||||
]
|
||||
: [];
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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*"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -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:
|
||||
|
|
@ -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
|
||||
|
|
@ -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** |
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
Loading…
Reference in New Issue