diff --git a/.claude/mcp-config.json b/.claude/mcp-config.json index 1bd7847..e071491 100644 --- a/.claude/mcp-config.json +++ b/.claude/mcp-config.json @@ -9,7 +9,7 @@ "command": "npx", "args": ["-y", "@modelcontextprotocol/server-github"], "env": { - "GITHUB_PERSONAL_ACCESS_TOKEN": "YOUR_GITHUB_TOKEN_HERE" + "GITHUB_PERSONAL_ACCESS_TOKEN": "${GITHUB_TOKEN}" }, "description": "GitHub MCP Server - Enables repository management, branch creation, commits, and PR operations" }, @@ -28,7 +28,7 @@ "command": "npx", "args": ["-y", "@neondatabase/mcp-server-neon"], "env": { - "NEON_API_KEY": "YOUR_NEON_API_KEY_HERE" + "NEON_API_KEY": "${NEON_API_KEY}" }, "description": "Neon MCP Server - Manage Neon PostgreSQL projects, branches, databases, and run SQL queries" }, @@ -36,7 +36,7 @@ "command": "npx", "args": ["-y", "@supabase/mcp-server"], "env": { - "SUPABASE_ACCESS_TOKEN": "YOUR_SUPABASE_ACCESS_TOKEN_HERE" + "SUPABASE_ACCESS_TOKEN": "${SUPABASE_ACCESS_TOKEN}" }, "description": "Supabase MCP Server - Manage Supabase projects, databases, tables, and run SQL queries" }, @@ -44,8 +44,8 @@ "command": "npx", "args": ["-y", "@cloudflare/mcp-server-cloudflare"], "env": { - "CLOUDFLARE_API_TOKEN": "YOUR_CLOUDFLARE_API_TOKEN_HERE", - "CLOUDFLARE_ACCOUNT_ID": "YOUR_CLOUDFLARE_ACCOUNT_ID_HERE" + "CLOUDFLARE_API_TOKEN": "${CLOUDFLARE_API_TOKEN}", + "CLOUDFLARE_ACCOUNT_ID": "${CLOUDFLARE_ACCOUNT_ID}" }, "description": "Cloudflare MCP Server - Manage Workers, Pages, D1, R2, KV, Hyperdrive, and other Cloudflare services" } diff --git a/.env.example b/.env.example index d89f32a..2651558 100644 --- a/.env.example +++ b/.env.example @@ -24,24 +24,26 @@ MCP_SERVER_PORT=3001 MCP_SERVER_URL=http://mcp-server:3001 # ============================================ -# Database Configuration +# Database Configuration (Neon) # ============================================ # PostgreSQL connection string -# Docker Compose: postgresql://USER:PASS@postgres:5432/healthcare_ai_db -# Cloudflare: Use Hyperdrive connection string (hyperdrive://HYPERDRIVE_ID) # Neon: postgresql://USER:PASS@ep-xxx.neon.tech/dbname?sslmode=require -DATABASE_URL=postgresql://healthcare_user:healthcare_pass@postgres:5432/healthcare_ai_db +# Cloudflare: Use Hyperdrive connection string (hyperdrive://HYPERDRIVE_ID) +DATABASE_URL=postgresql://neondb_owner:YOUR_PASSWORD@ep-xxx.neon.tech/neondb?sslmode=require + +# Neon API Key (for MCP server) +NEON_API_KEY=napi_your_neon_api_key_here # Individual database connection parameters (alternative to DATABASE_URL) -DB_HOST=postgres +DB_HOST=ep-xxx.neon.tech DB_PORT=5432 -DB_NAME=healthcare_ai_db -DB_USER=healthcare_user -DB_PASSWORD=healthcare_pass +DB_NAME=neondb +DB_USER=neondb_owner +DB_PASSWORD=your_password_here # ============================================ -# AI Service Configuration +# AI Service Configuration (Anthropic) # ============================================ # Anthropic API Key (Claude AI) @@ -55,6 +57,49 @@ ANTHROPIC_API_KEY=sk-ant-api03-your-key-here # AI request timeout in milliseconds (optional, default: 30000) # AI_TIMEOUT=30000 +# ============================================ +# Email Service (Resend) +# ============================================ + +# Resend API Key for transactional emails +# Get your key from: https://resend.com +RESEND_API_KEY=re_your_resend_api_key_here + +# ============================================ +# Payment Processing (Stripe) +# ============================================ + +# Stripe Test Keys (for development) +STRIPE_PUBLISHABLE_KEY=pk_test_your_publishable_key +STRIPE_SECRET_KEY=sk_test_your_secret_key +STRIPE_WEBHOOK_SECRET=whsec_your_webhook_secret + +# Stripe Live Keys (for production) +STRIPE_LIVE_PUBLISHABLE_KEY=pk_live_your_live_publishable_key +STRIPE_LIVE_SECRET_KEY=sk_live_your_live_secret_key + +# ============================================ +# Cache (Redis/Upstash) +# ============================================ + +# Upstash Redis REST API +UPSTASH_REDIS_REST_URL=https://your-redis.upstash.io +UPSTASH_REDIS_REST_TOKEN=your_upstash_token_here + +# Redis connection string (alternative) +REDIS_URL=rediss://default:your_token@your-redis.upstash.io:6379 + +# ============================================ +# Authentication & OAuth +# ============================================ + +# GitHub Personal Access Token +GITHUB_TOKEN=ghp_your_github_token_here + +# LinkedIn OAuth +LINKEDIN_CLIENT_ID=your_linkedin_client_id +LINKEDIN_CLIENT_SECRET=your_linkedin_client_secret + # ============================================ # CORS Configuration # ============================================ @@ -80,13 +125,23 @@ VITE_API_URL=http://localhost:3000 # ============================================ # Cloudflare Account ID (for deployment) -# CLOUDFLARE_ACCOUNT_ID=your-account-id +CLOUDFLARE_ACCOUNT_ID=your-account-id # Cloudflare API Token (for Wrangler CLI) -# CLOUDFLARE_API_TOKEN=your-api-token +CLOUDFLARE_API_TOKEN=your-api-token # Hyperdrive ID (for database connection pooling) -# HYPERDRIVE_ID=your-hyperdrive-id +HYPERDRIVE_ID=your-hyperdrive-id + +# ============================================ +# External Services +# ============================================ + +# Vercel Token (if using Vercel) +VERCEL_TOKEN=your_vercel_token_here + +# Render API Key (legacy, if needed) +# RENDER_API_KEY=your_render_api_key_here # ============================================ # Optional: Advanced Configuration @@ -122,7 +177,7 @@ VITE_API_URL=http://localhost:3000 # 1. Copy this file to .env in project root: # cp .env.example .env # -# 2. Update ANTHROPIC_API_KEY with your actual API key +# 2. Update the credentials with your actual values # # 3. Start services: # docker-compose up -d @@ -131,14 +186,15 @@ VITE_API_URL=http://localhost:3000 # docker-compose exec backend env | grep ANTHROPIC_API_KEY # CLOUDFLARE DEPLOYMENT: -# 1. Create accounts on Cloudflare and Neon (or Supabase) +# 1. Create accounts on Cloudflare and Neon # # 2. Set up Hyperdrive: # wrangler hyperdrive create renalguard-db --connection-string="YOUR_NEON_URL" # -# 3. Configure secrets in Cloudflare dashboard: -# - ANTHROPIC_API_KEY -# - DATABASE_URL (Hyperdrive connection string) +# 3. Configure secrets in Cloudflare dashboard or via CLI: +# wrangler pages secret put ANTHROPIC_API_KEY +# wrangler pages secret put DATABASE_URL +# (etc.) # # 4. Deploy frontend to Cloudflare Pages: # cd frontend && wrangler pages deploy dist @@ -156,3 +212,4 @@ VITE_API_URL=http://localhost:3000 # - Keep database passwords strong and unique # - Review .gitignore to ensure .env is excluded # - In Cloudflare, use encrypted secrets for sensitive values +# - Use test keys for development, live keys for production diff --git a/.github/workflows/cloudflare-deploy.yml b/.github/workflows/cloudflare-deploy.yml new file mode 100644 index 0000000..e966644 --- /dev/null +++ b/.github/workflows/cloudflare-deploy.yml @@ -0,0 +1,322 @@ +name: Deploy to Cloudflare + +on: + push: + branches: + - main + - master + pull_request: + branches: + - main + - master + workflow_dispatch: + inputs: + environment: + description: 'Deployment environment' + required: true + default: 'production' + type: choice + options: + - production + - preview + +env: + NODE_VERSION: '20' + CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} + CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} + +jobs: + # =========================================== + # Job 1: Lint and Test + # =========================================== + lint-and-test: + name: Lint & Test + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: ${{ env.NODE_VERSION }} + cache: 'npm' + cache-dependency-path: | + backend/package-lock.json + frontend/package-lock.json + mcp-server/package-lock.json + + - name: Install backend dependencies + working-directory: backend + run: npm ci + + - name: Install frontend dependencies + working-directory: frontend + run: npm ci + + - name: Install MCP server dependencies + working-directory: mcp-server + run: npm ci + + - name: Lint backend + working-directory: backend + run: npm run lint --if-present + + - name: Lint frontend + working-directory: frontend + run: npm run lint --if-present + + - name: Run backend tests + working-directory: backend + run: npm test --if-present + + - name: Run frontend tests + working-directory: frontend + run: npm test --if-present + + # =========================================== + # Job 2: Build Frontend + # =========================================== + build-frontend: + name: Build Frontend + runs-on: ubuntu-latest + needs: lint-and-test + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: ${{ env.NODE_VERSION }} + cache: 'npm' + cache-dependency-path: frontend/package-lock.json + + - name: Install dependencies + working-directory: frontend + run: npm ci + + - name: Build frontend + working-directory: frontend + env: + VITE_API_URL: ${{ vars.BACKEND_URL || 'https://renalguard-backend.cloudflare.dev' }} + run: npm run build + + - name: Upload frontend build artifact + uses: actions/upload-artifact@v4 + with: + name: frontend-dist + path: frontend/dist + retention-days: 1 + + # =========================================== + # Job 3: Build Backend Container + # =========================================== + build-backend: + name: Build Backend Container + runs-on: ubuntu-latest + needs: lint-and-test + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Login to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract metadata for Docker + id: meta + uses: docker/metadata-action@v5 + with: + images: ghcr.io/${{ github.repository }}/renalguard-backend + tags: | + type=ref,event=branch + type=ref,event=pr + type=sha,prefix= + type=raw,value=latest,enable={{is_default_branch}} + + - name: Build and push backend image + uses: docker/build-push-action@v5 + with: + context: . + file: ./Dockerfile + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max + platforms: linux/amd64 + + # =========================================== + # Job 4: Build MCP Server Container + # =========================================== + build-mcp-server: + name: Build MCP Server Container + runs-on: ubuntu-latest + needs: lint-and-test + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Login to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract metadata for Docker + id: meta + uses: docker/metadata-action@v5 + with: + images: ghcr.io/${{ github.repository }}/renalguard-mcp-server + tags: | + type=ref,event=branch + type=ref,event=pr + type=sha,prefix= + type=raw,value=latest,enable={{is_default_branch}} + + - name: Build and push MCP server image + uses: docker/build-push-action@v5 + with: + context: ./mcp-server + file: ./mcp-server/Dockerfile + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max + platforms: linux/amd64 + + # =========================================== + # Job 5: Deploy Frontend to Cloudflare Pages + # =========================================== + deploy-frontend: + name: Deploy Frontend to Cloudflare Pages + runs-on: ubuntu-latest + needs: build-frontend + if: github.event_name == 'push' || github.event_name == 'workflow_dispatch' + environment: + name: ${{ github.ref == 'refs/heads/main' && 'production' || 'preview' }} + url: ${{ steps.deploy.outputs.deployment-url }} + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Download frontend build artifact + uses: actions/download-artifact@v4 + with: + name: frontend-dist + path: frontend/dist + + - name: Deploy to Cloudflare Pages + id: deploy + uses: cloudflare/wrangler-action@v3 + with: + apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }} + accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} + command: pages deploy frontend/dist --project-name=renalguard-frontend --commit-dirty=true + + - name: Output deployment URL + run: | + echo "## Frontend Deployment" >> $GITHUB_STEP_SUMMARY + echo "Deployed to: ${{ steps.deploy.outputs.deployment-url }}" >> $GITHUB_STEP_SUMMARY + + # =========================================== + # Job 6: Deploy Containers (Production Only) + # =========================================== + deploy-containers: + name: Deploy Containers + runs-on: ubuntu-latest + needs: [build-backend, build-mcp-server, deploy-frontend] + if: github.ref == 'refs/heads/main' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch') + environment: + name: production + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Deploy notification + run: | + echo "## Container Deployment" >> $GITHUB_STEP_SUMMARY + echo "Backend and MCP Server containers built and pushed to GHCR." >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "### Images:" >> $GITHUB_STEP_SUMMARY + echo "- Backend: ghcr.io/${{ github.repository }}/renalguard-backend:latest" >> $GITHUB_STEP_SUMMARY + echo "- MCP Server: ghcr.io/${{ github.repository }}/renalguard-mcp-server:latest" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "### Next Steps:" >> $GITHUB_STEP_SUMMARY + echo "Deploy these images to Cloudflare Containers via the Cloudflare Dashboard or API." >> $GITHUB_STEP_SUMMARY + + # =========================================== + # Job 7: Preview Deployment (PRs) + # =========================================== + preview-deployment: + name: Preview Deployment + runs-on: ubuntu-latest + needs: build-frontend + if: github.event_name == 'pull_request' + environment: + name: preview + url: ${{ steps.deploy.outputs.deployment-url }} + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Download frontend build artifact + uses: actions/download-artifact@v4 + with: + name: frontend-dist + path: frontend/dist + + - name: Deploy preview to Cloudflare Pages + id: deploy + uses: cloudflare/wrangler-action@v3 + with: + apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }} + accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} + command: pages deploy frontend/dist --project-name=renalguard-frontend --branch=${{ github.head_ref }} --commit-dirty=true + + - name: Comment on PR with preview URL + uses: actions/github-script@v7 + with: + script: | + const url = '${{ steps.deploy.outputs.deployment-url }}'; + const body = `## πŸš€ Preview Deployment Ready!\n\n**Preview URL:** ${url}\n\nThis preview will be automatically updated when you push new commits to this PR.`; + + // Find existing comment + const { data: comments } = await github.rest.issues.listComments({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.issue.number, + }); + + const botComment = comments.find(comment => + comment.user.type === 'Bot' && + comment.body.includes('Preview Deployment Ready') + ); + + if (botComment) { + await github.rest.issues.updateComment({ + owner: context.repo.owner, + repo: context.repo.repo, + comment_id: botComment.id, + body: body + }); + } else { + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.issue.number, + body: body + }); + } diff --git a/.gitignore b/.gitignore index f814802..44d3843 100644 --- a/.gitignore +++ b/.gitignore @@ -21,6 +21,7 @@ claude-task-master/ .env.local .env.production .env.*.local +.dev.vars *.key *.pem secrets/ diff --git a/CLOUDFLARE_MIGRATION_PLAN.md b/CLOUDFLARE_MIGRATION_PLAN.md index 226204c..db4661d 100644 --- a/CLOUDFLARE_MIGRATION_PLAN.md +++ b/CLOUDFLARE_MIGRATION_PLAN.md @@ -638,10 +638,11 @@ Step-by-step deployment guide including: - [ ] 4.8 Test database connectivity from containers (user action required) ### Phase 5: CI/CD Pipeline -- [ ] 5.1 Create `.github/workflows/cloudflare-deploy.yml` -- [ ] 5.2 Configure GitHub secrets -- [ ] 5.3 Test deployment pipeline -- [ ] 5.4 Configure branch previews +- [x] 5.1 Create `.github/workflows/cloudflare-deploy.yml` +- [x] 5.2 Create CI/CD setup documentation (`docs/CICD_SETUP.md`) +- [x] 5.3 Configure branch previews (included in workflow) +- [ ] 5.4 Configure GitHub secrets (user action required) +- [ ] 5.5 Test deployment pipeline (user action required) ### Phase 6: Documentation - [ ] 6.1 Update `README.md` with Cloudflare info diff --git a/docs/CICD_SETUP.md b/docs/CICD_SETUP.md new file mode 100644 index 0000000..fd1c0a2 --- /dev/null +++ b/docs/CICD_SETUP.md @@ -0,0 +1,399 @@ +# RENALGUARD CI/CD Setup Guide + +This guide covers setting up the CI/CD pipeline for automated deployment to Cloudflare using GitHub Actions. + +## Table of Contents + +1. [Overview](#overview) +2. [Prerequisites](#prerequisites) +3. [GitHub Secrets Configuration](#github-secrets-configuration) +4. [GitHub Variables Configuration](#github-variables-configuration) +5. [Cloudflare Pages Setup](#cloudflare-pages-setup) +6. [Workflow Overview](#workflow-overview) +7. [Manual Deployment](#manual-deployment) +8. [Troubleshooting](#troubleshooting) + +--- + +## Overview + +The CI/CD pipeline automates: + +- **Linting and testing** on every push and PR +- **Building** frontend, backend, and MCP server +- **Deploying frontend** to Cloudflare Pages +- **Building container images** and pushing to GitHub Container Registry +- **Preview deployments** for pull requests + +### Deployment Flow + +``` +Push to main/master + β”‚ + β–Ό +β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” +β”‚ Lint & Test β”‚ +β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜ + β”‚ + β”Œβ”€β”€β”€β”€β”΄β”€β”€β”€β”€β” + β–Ό β–Ό +β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” +β”‚Frontendβ”‚ β”‚Backend β”‚ β”‚MCP Server β”‚ +β”‚ Build β”‚ β”‚ Build β”‚ β”‚ Build β”‚ +β””β”€β”€β”€β”¬β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”¬β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”˜ + β”‚ β”‚ β”‚ + β–Ό β–Ό β–Ό +β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” +β”‚CF Pagesβ”‚ β”‚ GitHub Container β”‚ +β”‚ Deploy β”‚ β”‚ Registry β”‚ +β””β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ +``` + +--- + +## Prerequisites + +Before setting up CI/CD, ensure you have: + +1. A GitHub repository with the RENALGUARD codebase +2. A Cloudflare account with: + - API Token with Pages and Workers permissions + - Account ID +3. (Optional) Neon or Supabase database configured + +--- + +## GitHub Secrets Configuration + +GitHub Secrets are encrypted environment variables. Configure these in your repository: + +**Settings β†’ Secrets and variables β†’ Actions β†’ Secrets β†’ New repository secret** + +### Required Secrets + +| Secret Name | Description | How to Get | +|-------------|-------------|------------| +| `CLOUDFLARE_API_TOKEN` | Cloudflare API token for deployments | [Create Token](#creating-cloudflare-api-token) | +| `CLOUDFLARE_ACCOUNT_ID` | Your Cloudflare account ID | [Find Account ID](#finding-cloudflare-account-id) | + +### Optional Secrets (for full deployment) + +| Secret Name | Description | How to Get | +|-------------|-------------|------------| +| `DATABASE_URL` | PostgreSQL connection string | From Neon/Supabase dashboard | +| `ANTHROPIC_API_KEY` | Claude AI API key | [console.anthropic.com](https://console.anthropic.com) | +| `NEON_API_KEY` | Neon API key (if using Neon) | Neon dashboard β†’ API Keys | +| `SUPABASE_ACCESS_TOKEN` | Supabase token (if using Supabase) | Supabase dashboard β†’ Access Tokens | + +### Creating Cloudflare API Token + +1. Go to [Cloudflare Dashboard](https://dash.cloudflare.com) +2. Click your profile icon β†’ **My Profile** +3. Go to **API Tokens** β†’ **Create Token** +4. Use the **Edit Cloudflare Workers** template or create custom: + - **Permissions:** + - Account: Cloudflare Pages: Edit + - Account: Workers Scripts: Edit + - Zone: Workers Routes: Edit (if using custom domains) + - **Account Resources:** Include your account +5. Click **Continue to summary** β†’ **Create Token** +6. Copy the token (you won't see it again!) + +### Finding Cloudflare Account ID + +1. Go to [Cloudflare Dashboard](https://dash.cloudflare.com) +2. Select any domain or go to **Workers & Pages** +3. Look in the right sidebar for **Account ID** +4. Or find it in the URL: `dash.cloudflare.com//...` + +--- + +## GitHub Variables Configuration + +Variables are non-sensitive configuration values: + +**Settings β†’ Secrets and variables β†’ Actions β†’ Variables β†’ New repository variable** + +| Variable Name | Description | Example | +|---------------|-------------|---------| +| `BACKEND_URL` | URL of deployed backend API | `https://renalguard-backend.cloudflare.dev` | + +--- + +## Cloudflare Pages Setup + +Before the first deployment, create the Pages project: + +### Option 1: Via Wrangler CLI + +```bash +# Login to Cloudflare +wrangler login + +# Create the Pages project +wrangler pages project create renalguard-frontend --production-branch main +``` + +### Option 2: Via Cloudflare Dashboard + +1. Go to [Cloudflare Dashboard](https://dash.cloudflare.com) +2. Click **Workers & Pages** β†’ **Create application** β†’ **Pages** +3. Select **Connect to Git** or **Direct Upload** +4. Name the project: `renalguard-frontend` +5. Configure build settings: + - **Build command:** `npm run build` + - **Build output directory:** `dist` + - **Root directory:** `frontend` + +### Custom Domain (Optional) + +1. Go to your Pages project +2. Click **Custom domains** β†’ **Set up a custom domain** +3. Enter your domain (e.g., `app.renalguard.com`) +4. Follow DNS configuration instructions + +--- + +## Workflow Overview + +### Triggers + +The workflow runs on: + +- **Push to main/master:** Full deployment +- **Pull requests:** Preview deployment +- **Manual dispatch:** On-demand deployment + +### Jobs + +| Job | Description | Runs On | +|-----|-------------|---------| +| `lint-and-test` | Lints and tests all packages | All triggers | +| `build-frontend` | Builds React frontend | After lint-and-test | +| `build-backend` | Builds backend Docker image | After lint-and-test | +| `build-mcp-server` | Builds MCP server Docker image | After lint-and-test | +| `deploy-frontend` | Deploys to Cloudflare Pages | Push to main only | +| `deploy-containers` | Notifies about container deployment | Push to main only | +| `preview-deployment` | Creates PR preview | Pull requests only | + +### Container Images + +Docker images are pushed to GitHub Container Registry (GHCR): + +- **Backend:** `ghcr.io///renalguard-backend:latest` +- **MCP Server:** `ghcr.io///renalguard-mcp-server:latest` + +To use these images: + +```bash +# Pull the images +docker pull ghcr.io///renalguard-backend:latest +docker pull ghcr.io///renalguard-mcp-server:latest + +# Run locally +docker run -p 3000:3000 ghcr.io///renalguard-backend:latest +``` + +--- + +## Manual Deployment + +### Trigger Workflow Manually + +1. Go to **Actions** tab in your repository +2. Select **Deploy to Cloudflare** workflow +3. Click **Run workflow** +4. Select branch and environment +5. Click **Run workflow** + +### Deploy Frontend Only + +```bash +cd frontend + +# Build +npm run build + +# Deploy to Cloudflare Pages +wrangler pages deploy dist --project-name=renalguard-frontend +``` + +### Deploy Containers Only + +```bash +# Build and push backend +docker build -t ghcr.io///renalguard-backend:latest . +docker push ghcr.io///renalguard-backend:latest + +# Build and push MCP server +docker build -t ghcr.io///renalguard-mcp-server:latest ./mcp-server +docker push ghcr.io///renalguard-mcp-server:latest +``` + +--- + +## Troubleshooting + +### Common Issues + +#### "Error: No account id provided" + +**Cause:** `CLOUDFLARE_ACCOUNT_ID` secret is missing or incorrect. + +**Solution:** +1. Verify the secret is set in repository settings +2. Check for typos in the secret name +3. Ensure the account ID is correct + +#### "Error: Authentication error" + +**Cause:** `CLOUDFLARE_API_TOKEN` is invalid or lacks permissions. + +**Solution:** +1. Create a new API token with correct permissions +2. Update the secret in repository settings +3. Verify token has Pages Edit permission + +#### "Error: Project not found" + +**Cause:** Cloudflare Pages project doesn't exist. + +**Solution:** +1. Create the project via Wrangler or Dashboard +2. Ensure project name matches (`renalguard-frontend`) + +#### Build fails with "npm ci" error + +**Cause:** Missing or corrupt package-lock.json. + +**Solution:** +```bash +# Regenerate lock files +rm -rf node_modules package-lock.json +npm install +git add package-lock.json +git commit -m "fix: Regenerate package-lock.json" +``` + +#### Container build fails + +**Cause:** Dockerfile or build context issues. + +**Solution:** +1. Test build locally: `docker build -t test .` +2. Check Dockerfile syntax +3. Verify all required files are present + +### Viewing Logs + +1. Go to **Actions** tab +2. Click on the failed workflow run +3. Expand the failed job +4. Click on the failed step to see logs + +### Re-running Failed Jobs + +1. Go to the failed workflow run +2. Click **Re-run jobs** β†’ **Re-run failed jobs** + +--- + +## Environment Protection Rules + +For production deployments, consider adding protection rules: + +1. Go to **Settings** β†’ **Environments** +2. Click on **production** +3. Configure: + - **Required reviewers:** Add team members + - **Wait timer:** Set delay before deployment + - **Deployment branches:** Restrict to `main` + +--- + +## Workflow Customization + +### Adding Tests + +Edit `.github/workflows/cloudflare-deploy.yml`: + +```yaml +- name: Run backend tests + working-directory: backend + run: npm test + +- name: Run frontend tests + working-directory: frontend + run: npm test -- --coverage +``` + +### Adding Notifications + +Add Slack/Discord notifications: + +```yaml +- name: Notify Slack + uses: 8398a7/action-slack@v3 + with: + status: ${{ job.status }} + webhook_url: ${{ secrets.SLACK_WEBHOOK }} + if: always() +``` + +### Caching Dependencies + +The workflow already uses GitHub's built-in caching. To optimize further: + +```yaml +- name: Cache node modules + uses: actions/cache@v4 + with: + path: ~/.npm + key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} + restore-keys: | + ${{ runner.os }}-node- +``` + +--- + +## Security Best Practices + +1. **Rotate secrets regularly** - Update API tokens every 90 days +2. **Use environment protection** - Require approvals for production +3. **Limit token permissions** - Only grant necessary permissions +4. **Review Actions logs** - Monitor for suspicious activity +5. **Enable branch protection** - Require PR reviews before merge + +--- + +## Quick Reference + +### Required Secrets Checklist + +- [ ] `CLOUDFLARE_API_TOKEN` - Cloudflare API token +- [ ] `CLOUDFLARE_ACCOUNT_ID` - Cloudflare account ID + +### Useful Commands + +```bash +# Check Cloudflare Pages projects +wrangler pages project list + +# Check deployment status +wrangler pages deployment list --project-name=renalguard-frontend + +# View deployment logs +wrangler pages deployment tail --project-name=renalguard-frontend +``` + +### Workflow Status Badges + +Add to your README.md: + +```markdown +![Deploy to Cloudflare](https://github.com///actions/workflows/cloudflare-deploy.yml/badge.svg) +``` + +--- + +*Last Updated: January 2026* diff --git a/scripts/setup-cloudflare-secrets.sh b/scripts/setup-cloudflare-secrets.sh new file mode 100755 index 0000000..9ff39c0 --- /dev/null +++ b/scripts/setup-cloudflare-secrets.sh @@ -0,0 +1,113 @@ +#!/bin/bash +# RENALGUARD - Cloudflare Secrets Setup Script +# This script sets up all environment variables for Cloudflare deployment +# +# USAGE: +# 1. Copy backend/.dev.vars.example to backend/.dev.vars and fill in your credentials +# 2. Source the .dev.vars file: source backend/.dev.vars +# 3. Run this script: ./scripts/setup-cloudflare-secrets.sh +# +# Or set environment variables directly before running this script. + +set -e + +# Check required environment variables +if [ -z "$CLOUDFLARE_API_TOKEN" ]; then + echo "ERROR: CLOUDFLARE_API_TOKEN environment variable is required" + echo "Please set it or source your .dev.vars file first" + exit 1 +fi + +if [ -z "$CLOUDFLARE_ACCOUNT_ID" ]; then + echo "ERROR: CLOUDFLARE_ACCOUNT_ID environment variable is required" + echo "Please set it or source your .dev.vars file first" + exit 1 +fi + +# Project name +PAGES_PROJECT="renalguard-frontend" + +echo "================================================" +echo "RENALGUARD - Cloudflare Secrets Setup" +echo "================================================" +echo "" +echo "Account ID: $CLOUDFLARE_ACCOUNT_ID" +echo "Project: $PAGES_PROJECT" +echo "" + +# Function to set a secret for Pages +set_pages_secret() { + local name=$1 + local value=$2 + if [ -n "$value" ]; then + echo "Setting Pages secret: $name" + echo "$value" | npx wrangler pages secret put "$name" --project-name="$PAGES_PROJECT" 2>/dev/null || echo " (Warning: Could not set secret - project may not exist yet)" + else + echo "Skipping $name (not set)" + fi +} + +echo "" +echo "================================================" +echo "Setting up secrets..." +echo "================================================" +echo "" + +# Database Configuration (Neon) +echo "--- Database (Neon) ---" +set_pages_secret "DATABASE_URL" "$DATABASE_URL" +set_pages_secret "NEON_API_KEY" "$NEON_API_KEY" + +# AI Configuration (Anthropic) +echo "" +echo "--- AI (Anthropic) ---" +set_pages_secret "ANTHROPIC_API_KEY" "$ANTHROPIC_API_KEY" + +# Email Service (Resend) +echo "" +echo "--- Email (Resend) ---" +set_pages_secret "RESEND_API_KEY" "$RESEND_API_KEY" + +# Payment Processing (Stripe) +echo "" +echo "--- Payments (Stripe) ---" +set_pages_secret "STRIPE_PUBLISHABLE_KEY" "$STRIPE_PUBLISHABLE_KEY" +set_pages_secret "STRIPE_SECRET_KEY" "$STRIPE_SECRET_KEY" +set_pages_secret "STRIPE_WEBHOOK_SECRET" "$STRIPE_WEBHOOK_SECRET" +set_pages_secret "STRIPE_LIVE_SECRET_KEY" "$STRIPE_LIVE_SECRET_KEY" +set_pages_secret "STRIPE_LIVE_PUBLISHABLE_KEY" "$STRIPE_LIVE_PUBLISHABLE_KEY" + +# Cache (Redis/Upstash) +echo "" +echo "--- Cache (Redis/Upstash) ---" +set_pages_secret "UPSTASH_REDIS_REST_URL" "$UPSTASH_REDIS_REST_URL" +set_pages_secret "UPSTASH_REDIS_REST_TOKEN" "$UPSTASH_REDIS_REST_TOKEN" +set_pages_secret "REDIS_URL" "$REDIS_URL" + +# GitHub Integration +echo "" +echo "--- GitHub ---" +set_pages_secret "GITHUB_TOKEN" "$GITHUB_TOKEN" + +# LinkedIn OAuth +echo "" +echo "--- LinkedIn OAuth ---" +set_pages_secret "LINKEDIN_CLIENT_ID" "$LINKEDIN_CLIENT_ID" +set_pages_secret "LINKEDIN_CLIENT_SECRET" "$LINKEDIN_CLIENT_SECRET" + +# Vercel (if needed) +echo "" +echo "--- Vercel ---" +set_pages_secret "VERCEL_TOKEN" "$VERCEL_TOKEN" + +echo "" +echo "================================================" +echo "Secrets setup complete!" +echo "================================================" +echo "" +echo "Note: If secrets failed, ensure the Cloudflare Pages project exists." +echo "Create it with: npx wrangler pages project create $PAGES_PROJECT" +echo "" +echo "To verify secrets were set:" +echo " npx wrangler pages secret list --project-name=$PAGES_PROJECT" +echo ""