# Beck Cabinet Company — Portal
## Cloud Deployment Guide  ·  AWS / Azure / Google Cloud

---

### Overview

This app runs as a Node.js container. The same Docker image deploys to any of the three major cloud providers. Choose one based on what your IT company is most comfortable with — the application behaves identically on all three.

```
beckportal.com  (GoDaddy DNS)
         │
         ▼
  Cloud Load Balancer  (HTTPS termination)
         │
         ▼
  Container / App Service  (Node.js, port 3000)
         │
         ▼
  Persistent Volume  (SQLite databases: users + sessions)
         │
         ▼
  Monday.com API  (live project data)
```

---

## Part 1 — Before you deploy (all providers)

### Step 1 — Generate your secrets

Run these three commands and save the output somewhere secure
(you'll paste them into your cloud provider's environment variable settings):

```bash
node -e "console.log('TOKEN_SECRET=' + require('crypto').randomBytes(64).toString('hex'))"
node -e "console.log('SESSION_SECRET=' + require('crypto').randomBytes(64).toString('hex'))"
node -e "console.log('ADMIN_KEY=' + require('crypto').randomBytes(32).toString('hex'))"
```

### Step 2 — Get your Monday.com credentials

- **API token**: Monday.com → click your avatar → Admin → API → copy token
- **Board ID**: open your project board → the number in the URL:
  `monday.com/boards/`**1234567890**

### Step 3 — Edit monday-config.js

Map your Monday column IDs to portal fields. Run this locally first to see your columns:

```bash
npm install
MONDAY_API_TOKEN=your_token MONDAY_BOARD_ID=your_id node scripts/list-columns.js
```

---

## Part 2 — AWS Deployment

**Recommended service: AWS App Runner** (simplest) or **ECS Fargate** (more control)

### Option A — AWS App Runner (easiest)

1. **Push code to GitHub** (or use App Runner's direct source deploy)

2. **Open AWS Console → App Runner → Create service**
   - Source: GitHub repository (connect your repo)
   - Runtime: Docker
   - Port: 3000

3. **Set environment variables** in App Runner configuration:
   ```
   NODE_ENV          = production
   MONDAY_API_TOKEN  = (your token)
   MONDAY_BOARD_ID   = (your board ID)
   TOKEN_SECRET      = (generated above)
   SESSION_SECRET    = (generated above)
   ADMIN_KEY         = (generated above)
   BASE_URL          = https://beckportal.com
   SESSION_DURATION  = 8h
   DATA_DIR          = /data
   ```

4. **Add persistent storage (EFS)**
   - Create an EFS file system in the same region
   - In App Runner → Configuration → Storage → attach EFS at mount point `/data`
   - This is where user accounts and sessions are stored — it persists across deployments

5. **Note the App Runner URL** (e.g. `abc123.us-east-1.awsapprunner.com`)
   — you'll use this for DNS

### Option B — ECS Fargate

1. **Build and push Docker image to ECR:**
   ```bash
   aws ecr create-repository --repository-name beck-cabinet-portal
   aws ecr get-login-password | docker login --username AWS --password-stdin YOUR_ACCOUNT.dkr.ecr.REGION.amazonaws.com
   docker build -t beck-cabinet-portal .
   docker tag beck-cabinet-portal:latest YOUR_ACCOUNT.dkr.ecr.REGION.amazonaws.com/beck-cabinet-portal:latest
   docker push YOUR_ACCOUNT.dkr.ecr.REGION.amazonaws.com/beck-cabinet-portal:latest
   ```

2. **Create ECS Cluster** → Fargate → create Task Definition
   - Container: point to your ECR image
   - Port mapping: 3000
   - Environment variables: same list as Option A
   - Mount EFS volume at `/data`

3. **Create ALB (Application Load Balancer)**
   - HTTPS listener on port 443 → target group → your ECS service
   - Request an SSL certificate via AWS Certificate Manager for `beckportal.com`

4. **Note the ALB DNS name** for DNS setup

### AWS — Create first admin user

```bash
# SSH into a running container or use ECS Exec:
aws ecs execute-command \
  --cluster beck-portal-cluster \
  --task TASK_ID \
  --container beck-cabinet-portal \
  --interactive \
  --command "node scripts/create-user.js"
```

---

## Part 3 — Azure Deployment

**Recommended service: Azure Container Apps** (easiest) or **Azure App Service**

### Option A — Azure Container Apps

1. **Install Azure CLI and log in:**
   ```bash
   az login
   az extension add --name containerapp
   ```

2. **Create resource group and Container Apps environment:**
   ```bash
   az group create --name beck-portal-rg --location westus2
   az containerapp env create \
     --name beck-portal-env \
     --resource-group beck-portal-rg \
     --location westus2
   ```

3. **Build and push to Azure Container Registry:**
   ```bash
   az acr create --name beckportalacr --resource-group beck-portal-rg --sku Basic
   az acr build --registry beckportalacr --image beck-cabinet-portal:latest .
   ```

4. **Deploy the Container App:**
   ```bash
   az containerapp create \
     --name beck-cabinet-portal \
     --resource-group beck-portal-rg \
     --environment beck-portal-env \
     --image beckportalacr.azurecr.io/beck-cabinet-portal:latest \
     --target-port 3000 \
     --ingress external \
     --env-vars \
       NODE_ENV=production \
       MONDAY_API_TOKEN=secretref:monday-token \
       MONDAY_BOARD_ID=YOUR_BOARD_ID \
       TOKEN_SECRET=secretref:token-secret \
       SESSION_SECRET=secretref:session-secret \
       ADMIN_KEY=secretref:admin-key \
       BASE_URL=https://beckportal.com \
       DATA_DIR=/data
   ```

5. **Add Azure Files persistent storage:**
   - Create a Storage Account → File Share named `beck-portal-data`
   - Mount it at `/data` in your Container App configuration

6. **Note the Container App URL** (e.g. `beck-cabinet-portal.lemonbeach-abc123.westus2.azurecontainerapps.io`)

### Option B — Azure App Service

1. Create an App Service Plan (Linux, B1 or higher)
2. Create a Web App → Docker Container → your ACR image
3. Configuration → Application Settings → add all environment variables
4. Add Azure Files mount under Configuration → Path mappings → `/data`
5. Note the `.azurewebsites.net` URL for DNS

### Azure — Create first admin user

```bash
# Use the Container App console in Azure Portal, or:
az containerapp exec \
  --name beck-cabinet-portal \
  --resource-group beck-portal-rg \
  --command "node scripts/create-user.js"
```

---

## Part 4 — Google Cloud Deployment

**Recommended service: Cloud Run** (easiest, scales to zero = lowest cost)

### Cloud Run

1. **Install gcloud CLI and authenticate:**
   ```bash
   gcloud auth login
   gcloud config set project YOUR_PROJECT_ID
   ```

2. **Enable required APIs:**
   ```bash
   gcloud services enable run.googleapis.com containerregistry.googleapis.com
   ```

3. **Build and push image:**
   ```bash
   gcloud builds submit --tag gcr.io/YOUR_PROJECT_ID/beck-cabinet-portal
   ```

4. **Store secrets in Secret Manager:**
   ```bash
   echo -n "your_monday_token" | gcloud secrets create monday-api-token --data-file=-
   echo -n "your_token_secret" | gcloud secrets create token-secret --data-file=-
   echo -n "your_session_secret" | gcloud secrets create session-secret --data-file=-
   echo -n "your_admin_key" | gcloud secrets create admin-key --data-file=-
   ```

5. **Deploy to Cloud Run:**
   ```bash
   gcloud run deploy beck-cabinet-portal \
     --image gcr.io/YOUR_PROJECT_ID/beck-cabinet-portal \
     --platform managed \
     --region us-central1 \
     --port 3000 \
     --allow-unauthenticated \
     --set-env-vars NODE_ENV=production,MONDAY_BOARD_ID=YOUR_ID,BASE_URL=https://beckportal.com,DATA_DIR=/data \
     --set-secrets MONDAY_API_TOKEN=monday-api-token:latest,TOKEN_SECRET=token-secret:latest,SESSION_SECRET=session-secret:latest,ADMIN_KEY=admin-key:latest
   ```

6. **Add persistent storage (Filestore or Cloud Storage FUSE):**
   - For SQLite persistence, use **Cloud Filestore** (NFS) mounted at `/data`
   - Alternatively use Cloud SQL if you want to migrate from SQLite to Postgres later

7. **Note the Cloud Run URL** (e.g. `beck-cabinet-portal-abc123-uc.a.run.app`)

### GCP — Create first admin user

```bash
gcloud run jobs create create-admin \
  --image gcr.io/YOUR_PROJECT_ID/beck-cabinet-portal \
  --region us-central1 \
  --command "node,scripts/create-user.js" \
  --set-env-vars NODE_ENV=production,DATA_DIR=/data \
  --set-secrets MONDAY_API_TOKEN=monday-api-token:latest

gcloud run jobs execute create-admin --region us-central1 --wait
```

---

## Part 5 — Point beckportal.com to your cloud app (GoDaddy)

This step is the same regardless of which cloud provider you chose.

1. **Log into GoDaddy** → My Products → your domain → DNS

2. **Add a CNAME record:**

   | Type  | Name   | Value                                    | TTL  |
   |-------|--------|------------------------------------------|------|
   | CNAME | portal | *your-cloud-app-url* (from steps above)  | 600  |

   For example:
   - AWS App Runner: `abc123.us-east-1.awsapprunner.com`
   - Azure Container Apps: `beck-cabinet-portal.lemonbeach-abc123.westus2.azurecontainerapps.io`
   - GCP Cloud Run: `beck-cabinet-portal-abc123-uc.a.run.app`

3. **SSL certificate** — all three cloud services provide HTTPS automatically.
   Once DNS propagates (5–30 minutes), `https://beckportal.com` will work.

4. **Verify:**
   ```bash
   curl https://beckportal.com/health
   # Should return: {"status":"ok","env":"production"}
   ```

---

## Part 6 — Set up Monday.com webhook

Makes the portal update instantly when you change a status, rather than waiting up to 5 minutes for the cache to expire.

1. In Monday.com, go to your board → Automations → Custom Automation
2. Trigger: "When status changes"
3. Action: Send webhook
4. URL: `https://beckportal.com/webhooks/monday`

---

## Security checklist for production

- [ ] `NODE_ENV=production` is set
- [ ] All three secrets (TOKEN_SECRET, SESSION_SECRET, ADMIN_KEY) are stored in cloud secret manager, not in code
- [ ] Persistent volume is attached at `DATA_DIR` so user accounts survive redeployments
- [ ] HTTPS is working (`curl https://beckportal.com/health` returns 200)
- [ ] Admin panel is only accessible to your team (consider IP allowlist if your cloud provider supports it)
- [ ] First admin user created via `create-user` script
- [ ] Monday.com webhook configured

---

## Cost estimates (approximate monthly)

| Provider | Service | Est. cost |
|---|---|---|
| AWS | App Runner (0.25 vCPU, 0.5GB) + EFS | ~$25–40/mo |
| Azure | Container Apps (consumption plan) | ~$10–30/mo |
| GCP | Cloud Run (scales to zero) | ~$5–20/mo |

All three include free SSL certificates. GCP Cloud Run is lowest cost for low-traffic apps since it scales to zero when no one is using the portal.

---

## Troubleshooting

| Problem | Fix |
|---|---|
| Health check fails | Container not starting — check logs for missing env vars |
| Login doesn't persist | DATA_DIR not mounted — sessions lost on restart |
| "Project not found" | TOKEN_SECRET changed since links were generated |
| Portal shows old data | Webhook not set up — changes take up to 5 min to appear |
| HTTPS not working | DNS hasn't propagated yet — wait 30 min, then check |
| Admin password forgotten | Re-run `create-user` script to create a new account |
