# FinOps Query Gateway — Deployment Guide

Deploy the Gateway on a GCP VM with Docker Compose, Nginx reverse proxy, and HTTPS.

## Prerequisites

- GCP VM with Docker & Docker Compose installed
- DNS record `cloudcost-api.pingcap.net` pointing to the VM
- GCP Service Account JSON with BigQuery read access
- GCP OAuth 2.0 Client ID (for Google SSO login)
- LLM API key (Gemini or OpenRouter)

## 1. Install Docker (if needed)

```bash
sudo apt-get update && sudo apt-get install -y docker.io docker-compose-plugin
sudo usermod -aG docker $USER && newgrp docker
```

## 2. Clone the repo

```bash
git clone <repo-url> ~/finops-sql && cd ~/finops-sql
git checkout purelind/finops-query-gateway
```

## 3. Prepare GCP Service Account JSON

The SA needs BigQuery Data Viewer and BigQuery Job User roles on project `gcp-rd-research-dev`.

```bash
sudo mkdir -p /opt/finops
sudo cp /path/to/sa.json /opt/finops/sa.json
sudo chmod 600 /opt/finops/sa.json
```

## 4. Create `.env`

```bash
cat > .env << 'EOF'
# === Required ===
POSTGRES_PASSWORD=<strong-password>
GOOGLE_AUTH_CLIENT_ID=<GCP OAuth 2.0 Client ID>

# LLM — at least one is required, otherwise SQL generation will fail
GEMINI_API_KEY=<Gemini API Key>
# OPENROUTER_API_KEY=<alternative>

# === Optional (defaults shown) ===
# LLM_MODEL=google/gemini-3-flash-preview
# SA_JSON_PATH=/opt/finops/sa.json
EOF
```

### How to get `GOOGLE_AUTH_CLIENT_ID`

1. GCP Console → APIs & Services → Credentials
2. Create **OAuth 2.0 Client ID** (Web application type)
3. Authorized JavaScript Origins: `https://cloudcost-api.pingcap.net`
4. Copy the Client ID

## 5. Build and start

```bash
docker compose up -d --build
```

## 6. Initialize database

```bash
docker compose exec finops-query-gateway npx prisma migrate deploy
```

## 7. Create the first admin user

Option A — Let user sign in via `/auth/login` (auto-creates `viewer`), then promote:

```bash
docker compose exec postgres psql -U finops finops_gateway -c "
UPDATE users SET role = 'super_admin' WHERE email = 'your-email@pingcap.com';
"
```

Option B — Insert directly:

```bash
docker compose exec postgres psql -U finops finops_gateway -c "
INSERT INTO users (id, email, name, role, status, auth_provider, created_at, updated_at)
VALUES (gen_random_uuid(), 'your-email@pingcap.com', 'Your Name', 'super_admin', 'active', 'manual', now(), now());
"
```

## 8. Configure Nginx reverse proxy (HTTPS)

```bash
sudo apt-get install -y nginx
```

Place your SSL certificate and key on the VM:

```bash
sudo mkdir -p /etc/nginx/ssl
sudo cp /path/to/cloudcost-api.pingcap.net.crt /etc/nginx/ssl/
sudo cp /path/to/cloudcost-api.pingcap.net.key /etc/nginx/ssl/
sudo chmod 600 /etc/nginx/ssl/*.key
```

Create Nginx site config:

```bash
sudo tee /etc/nginx/sites-available/cloudcost-api << 'NGINX'
server {
    listen 80;
    server_name cloudcost-api.pingcap.net;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl;
    server_name cloudcost-api.pingcap.net;

    ssl_certificate     /etc/nginx/ssl/cloudcost-api.pingcap.net.crt;
    ssl_certificate_key /etc/nginx/ssl/cloudcost-api.pingcap.net.key;

    location / {
        proxy_pass http://127.0.0.1:4000;
        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;
    }
}
NGINX

sudo ln -sf /etc/nginx/sites-available/cloudcost-api /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx
```

## 9. Verify

```bash
# Health check
curl -s https://cloudcost-api.pingcap.net/healthz
# {"status":"ok"}

# Setup page
curl -s https://cloudcost-api.pingcap.net/setup | head -5
# # CloudCost Skill — Install & Configure

# Skill file download
curl -s https://cloudcost-api.pingcap.net/skill/SKILL.md | head -5
# ---
# name: cloudcost
```

## Updating

```bash
cd ~/finops-sql
git pull
docker compose up -d --build

# If there are new database migrations:
docker compose exec finops-query-gateway npx prisma migrate deploy
```

## Environment Variables Reference

| Variable | Required | Default | Description |
|----------|----------|---------|-------------|
| `DATABASE_URL` | auto | (from docker-compose) | PostgreSQL connection string |
| `POSTGRES_PASSWORD` | yes | `finops_dev` | PostgreSQL password |
| `GATEWAY_PORT` | no | `4000` | HTTP port |
| `GOOGLE_AUTH_CLIENT_ID` | yes | — | Google OAuth Client ID |
| `GOOGLE_AUTH_DOMAIN` | no | `pingcap.com` | Allowed Google Workspace domain |
| `GOOGLE_AUTH_TOKEN_DAYS` | no | `90` | Token validity in days |
| `GOOGLE_APPLICATION_CREDENTIALS` | no | — | Path to GCP SA JSON (inside container) |
| `SA_JSON_PATH` | no | `/opt/finops/sa.json` | Path to SA JSON on host (mounted into container) |
| `BIGQUERY_PROJECT_ID` | no | `gcp-rd-research-dev` | GCP project for BigQuery |
| `GEMINI_API_KEY` | * | — | Gemini API key |
| `OPENROUTER_API_KEY` | * | — | OpenRouter API key |
| `LLM_MODEL` | no | `google/gemini-3-flash-preview` | LLM model identifier |
| `GATEWAY_QUERY_BYTES_LIMIT` | no | 10 GB | Max bytes per query |
| `GATEWAY_DAILY_BYTES_LIMIT` | no | 50 GB | Max bytes per user per day |
| `GATEWAY_MAX_ROWS` | no | `1000` | Max rows per response |
| `GATEWAY_TOKEN_MAX_DAYS` | no | `365` | Max API token validity |

\* At least one LLM API key is required.
