# FinOps Query Gateway — Deployment Runbook

## Prerequisites

- Docker (for PostgreSQL)
- Node.js 22+
- GCP Application Default Credentials (`gcloud auth application-default login`)
- OpenRouter API key (or Gemini API key)
- dbt marts already built in BigQuery

## 1. Start PostgreSQL

```bash
docker-compose up -d postgres
```

Verify: `docker exec finops-sql-postgres-1 pg_isready`

## 2. Configure Environment

Create `finops-query-gateway/.env.local`:

```env
DATABASE_URL=postgresql://finops:finops_dev@localhost:5432/finops_gateway
GATEWAY_PORT=4000
BIGQUERY_PROJECT_ID=gcp-rd-research-dev
OPENROUTER_API_KEY=sk-or-...
# LLM_MODEL=google/gemini-2.5-flash   (default)
```

## 3. Run Prisma Migrations

```bash
cd finops-query-gateway
npx prisma migrate deploy
npx prisma generate
```

## 4. Seed Admin User

```bash
GATEWAY_ADMIN_EMAIL=admin@pingcap.com \
GATEWAY_ADMIN_NAME=Admin \
npm run db:seed
```

**Save the printed token** — it won't be shown again. Format: `fqg_<64 hex chars>`.

## 5. Start Gateway

```bash
# Development
set -a && source .env.local && set +a && npm run dev

# Production
npm run build && set -a && source .env.local && set +a && npm start
```

Verify:

```bash
curl --noproxy '*' http://localhost:4000/healthz   # → {"status":"ok"}
curl --noproxy '*' http://localhost:4000/readyz     # → {"status":"ok"}
```

## 6. Create Additional Users & Tokens

```bash
TOKEN="fqg_..."  # admin token from step 4

# Create token for existing user
curl --noproxy '*' -s -X POST \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"user_email":"viewer@pingcap.com","name":"viewer-token","expires_in_days":90}' \
  http://localhost:4000/v1/admin/tokens
```

Note: Users must be created in the database first (no user creation API):

```sql
INSERT INTO users (id, email, name, role, status, created_at, updated_at)
VALUES (gen_random_uuid(), 'viewer@pingcap.com', 'Viewer', 'viewer', 'active', NOW(), NOW());
```

## 7. Configure Account Access Grants

For restricted production accounts, grant access per user:

```bash
curl --noproxy '*' -s -X POST \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"user_email":"viewer@pingcap.com","cloud_provider":"aws","account_id":"143458967504","expires_in_days":30}' \
  http://localhost:4000/v1/admin/grants
```

## 8. CLI Setup

```bash
export FINOPS_GATEWAY_URL=http://localhost:4000
export FINOPS_GATEWAY_TOKEN=fqg_...
export no_proxy="*"  # if using a local proxy

cd finops-cli
npx tsx src/index.ts analyses list
npx tsx src/index.ts ask "列出上个月成本最高的 10 个账户"
```

## API Reference (Quick)

| Endpoint | Method | Auth | Description |
|---|---|---|---|
| `/healthz` | GET | No | Liveness check |
| `/readyz` | GET | No | Readiness check (DB) |
| `/v1/catalog/semantic-schema` | GET | Yes | Dimensions, metrics, templates |
| `/v1/catalog/accounts` | GET | Yes | Visible accounts for user |
| `/v1/query/ask` | POST | Yes | NL → SQL → results |
| `/v1/query/analyses/:id` | POST | Yes | Run analysis template |
| `/v1/me/queries` | GET | Yes | User's query history |
| `/v1/admin/tokens` | POST | Admin | Create token |
| `/v1/admin/grants` | POST | Admin | Grant account access |
| `/v1/admin/audit` | GET | Admin | Query audit logs |

## Known Limitations

1. **LLM date handling**: LLM may generate invalid dates (e.g., Feb 29 in non-leap years). The `enforceConstraints()` function normalizes all dates to first-of-month.
2. **account_purpose filter**: Values are case-sensitive (`"Production"`, `"Development"`, `"ProductionInfrastructure"`, `"Others"`). The system prompt includes these values.
3. **OpenRouter model names**: Default is `google/gemini-2.5-flash`. Verify the model ID is valid on OpenRouter if changing `LLM_MODEL`.
