From d8fdf621b7ed854ad0ea91f5bcf179d70296d6b5 Mon Sep 17 00:00:00 2001 From: Pyronewbic Date: Mon, 11 May 2026 18:09:05 +0530 Subject: [PATCH] chore: update READMEs, CHANGELOG, Terraform for casecomp-site Cloud Run + CDN --- CHANGELOG.md | 21 ++++++++++---- README.md | 5 ++-- terraform/README.md | 30 ++++++++++++++------ terraform/main.tf | 67 +++++++++++++++++++++++++++++++++++++++++---- 4 files changed, 101 insertions(+), 22 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index de41de9..febb31c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,19 +7,28 @@ Initial public beta. ### Added - Web dashboard with AI pre-grading detail panel, PSA signal bar, source filters - Multi-source slab search: compare PSA 10 prices across eBay, magi.camp, Yahoo Auctions -- AI pre-grading from listing photos (centering, corners, edges, surface + confidence) +- Per-subgrade AI grading: centering, corners, edges, surface graded independently in parallel +- Front + back image analysis with subgradeDetails (score, confidence, detail per attribute) - PSA tier recommendations (Value/Regular/Express) with reasoning per card value -- REST API with CC_LIVE_ key auth, rate limiting (60/min auth, 20/min sample data), error monitoring +- REST API with CC_LIVE_ key auth + CC_LIVE_SANDBOX_ public sandbox key +- Rate limiting: 60/min auth, 20/min sample data, 5/min sandbox - Firestore caching with stale-while-revalidate, per-key cache isolation - Magi search migrated from Playwright to fetch+cheerio (~10x faster) - eBay sold scrape retry with backoff on 503 +- OAuth token pre-fetched on server startup +- Security: helmet headers, error sanitization, request IDs, trust proxy - 105 tests (63 unit + 42 API integration) -- GitHub Actions CI on push/PR +- GitHub Actions CI on push/PR, auto-deploy on merge to main - Chrome extension: queue auto-join for Pokemon Center, Walmart, Costco, Target - Claude Code `/casecomp` skill for plain-English card search +- GitHub release v1.0.0-beta.1 with Chrome extension zip ### Infrastructure -- GCP Cloud Run (asia-south1), Firestore, HTTPS load balancer, managed SSL -- Secret Manager for API keys (EBAY, ANTHROPIC, PSA, CASECOMP) -- Cloud Monitoring alert policy (email on >5 errors/5min) +- Cloud Run `casecomp-api` (API) + `casecomp-site` (frontend SSR with Cloud CDN) +- HTTPS LB routes by host: casecomp.xyz → site, api.casecomp.xyz → API +- Firestore, managed SSL certs, Secret Manager (incl. sandbox key) +- Cloud Monitoring: error alerts + uptime check on /api/health - Terraform with GCS state backend +- Workload Identity Federation for GitHub Actions → GCP (no stored keys) +- Kaniko layer caching for Cloud Build +- Branch protection on main: CI required before merge diff --git a/README.md b/README.md index 6d3df9b..32219bf 100644 --- a/README.md +++ b/README.md @@ -65,7 +65,7 @@ lib/ demo.js Sample data (3 cards with real listings) swagger.js OpenAPI 3.0.3 spec extension/ Chrome extension: queue auto-join, drop intel -terraform/ GCP infra: Cloud Run, Firestore, LB, Secret Manager +terraform/ GCP infra: Cloud Run ×2, Firestore, LB + CDN, Secret Manager test/ unit-test.js 63 unit tests (filters, grading, query, demo data) api-test.js 42 API integration tests @@ -172,6 +172,7 @@ EBAY_CLIENT_SECRET= # required ANTHROPIC_API_KEY= # AI grading + magi translation PSA_AUTH_TOKEN= # PSA pop reports CASECOMP_API_KEY= # API auth (CC_LIVE_ prefix) +CASECOMP_SANDBOX_KEY= # public sandbox key (CC_LIVE_SANDBOX_ prefix, 5/min) ``` In production, secrets are stored in GCP Secret Manager and referenced by Cloud Run. @@ -191,7 +192,7 @@ All caches use Firestore (shared across Cloud Run instances, persists across dep ## Infrastructure -GCP (Terraform managed): Cloud Run (asia-south1), Firestore, HTTPS load balancer with managed SSL, Secret Manager, Cloud Monitoring alerts. State stored in GCS bucket. See `terraform/`. +GCP (Terraform managed): Cloud Run `casecomp-api` (API) + `casecomp-site` (frontend SSR), Firestore, HTTPS load balancer with Cloud CDN, managed SSL, Secret Manager, Cloud Monitoring alerts. State in GCS bucket. Same LB IP routes by host: `casecomp.xyz` → site, `api.casecomp.xyz` → API. See `terraform/`. ## Chrome Extension diff --git a/terraform/README.md b/terraform/README.md index 88169ef..db8e155 100644 --- a/terraform/README.md +++ b/terraform/README.md @@ -1,26 +1,37 @@ # Terraform — Casecomp Infrastructure -GCP infrastructure for the Casecomp API. State is stored in a GCS bucket (`casecomp-terraform-state`). +GCP infrastructure for Casecomp. State is stored in a GCS bucket (`casecomp-terraform-state`). ## Resources | Resource | Purpose | |----------|---------| -| Cloud Run (`cardscrapebot`) | API + dashboard, asia-south1, scales to 20 instances | +| Cloud Run `casecomp-api` | API + dashboard, asia-south1, scales to 20 instances | +| Cloud Run `casecomp-site` | Frontend SSR (TanStack Start), scales to 10 instances | | Firestore | Grade logs, drops, webhooks, alerts, all caches | -| HTTPS Load Balancer | Global IP, managed SSL cert, URL map, backend service | -| Secret Manager | EBAY_CLIENT_ID/SECRET, ANTHROPIC_API_KEY, PSA_AUTH_TOKEN, CASECOMP_API_KEY | -| Cloud Monitoring | Log-based metric on `[ERROR]`, email alert on >5 errors/5min | +| HTTPS Load Balancer | Global IP (`34.107.143.136`), URL map routes by host | +| Cloud CDN | Caches static assets from frontend Cloud Run | +| SSL Certificates | Managed certs for `api.casecomp.xyz` and `casecomp.xyz` + `www` | +| GCS Bucket `casecomp-site` | (Legacy) Static site bucket, replaced by Cloud Run SSR | +| Secret Manager | EBAY_CLIENT_ID/SECRET, ANTHROPIC_API_KEY, PSA_AUTH_TOKEN, CASECOMP_API_KEY, CASECOMP_SANDBOX_KEY | +| Cloud Monitoring | Log-based metric on `[ERROR]`, error + uptime alerts → email | | APIs enabled | Cloud Run, Compute, Firestore, Cloud Build, Secret Manager, Monitoring | +## Routing + +Same LB IP, routed by hostname: +- `casecomp.xyz` / `www.casecomp.xyz` → Cloud Run `casecomp-site` (CDN enabled) +- `api.casecomp.xyz` → Cloud Run `casecomp-api` + ## Variables | Variable | Default | Description | |----------|---------|-------------| | `project_id` | `casecomp-495718` | GCP project | | `region` | `asia-south1` | Deploy region | -| `domain` | `api.casecomp.xyz` | SSL cert domain | -| `container_image` | `gcr.io/casecomp-495718/cardscrapebot` | Docker image | +| `api_domain` | `api.casecomp.xyz` | API SSL cert domain | +| `site_domain` | `casecomp.xyz` | Frontend SSL cert domain | +| `container_image` | `gcr.io/casecomp-495718/casecomp-api` | API Docker image | | `alert_email` | *(sensitive, in terraform.tfvars)* | Monitoring alert recipient | ## Usage @@ -37,7 +48,10 @@ If resources were created manually (outside Terraform), import them before apply ```bash terraform import google_cloud_run_v2_service.api \ - "projects/casecomp-495718/locations/asia-south1/services/cardscrapebot" + "projects/casecomp-495718/locations/asia-south1/services/casecomp-api" + +terraform import google_cloud_run_v2_service.site \ + "projects/casecomp-495718/locations/asia-south1/services/casecomp-site" ``` ## Files diff --git a/terraform/main.tf b/terraform/main.tf index 94cb78a..dddf95e 100644 --- a/terraform/main.tf +++ b/terraform/main.tf @@ -196,15 +196,70 @@ resource "google_compute_backend_service" "api_backend" { } } -resource "google_compute_backend_bucket" "site_backend" { - name = "casecomp-site-backend" - bucket_name = google_storage_bucket.site.name - enable_cdn = true +resource "google_cloud_run_v2_service" "site" { + name = "casecomp-site" + location = var.region + + template { + scaling { + max_instance_count = 10 + } + + containers { + image = "gcr.io/${var.project_id}/casecomp-site" + + ports { + container_port = 8080 + } + + resources { + limits = { + cpu = "1000m" + memory = "512Mi" + } + } + } + } + + depends_on = [google_project_service.run] +} + +resource "google_cloud_run_v2_service_iam_member" "site_public" { + name = google_cloud_run_v2_service.site.name + location = var.region + role = "roles/run.invoker" + member = "allUsers" +} + +resource "google_compute_region_network_endpoint_group" "site_neg" { + name = "casecomp-site-neg" + region = var.region + network_endpoint_type = "SERVERLESS" + + cloud_run { + service = google_cloud_run_v2_service.site.name + } +} + +resource "google_compute_backend_service" "site_backend" { + name = "casecomp-site-backend" + enable_cdn = true + + cdn_policy { + cache_mode = "CACHE_ALL_STATIC" + default_ttl = 3600 + max_ttl = 86400 + signed_url_cache_max_age_sec = 0 + } + + backend { + group = google_compute_region_network_endpoint_group.site_neg.id + } } resource "google_compute_url_map" "api_urlmap" { name = "cardscrapebot-urlmap" - default_service = google_compute_backend_bucket.site_backend.id + default_service = google_compute_backend_service.site_backend.id host_rule { hosts = [var.api_domain] @@ -223,7 +278,7 @@ resource "google_compute_url_map" "api_urlmap" { path_matcher { name = "site" - default_service = google_compute_backend_bucket.site_backend.id + default_service = google_compute_backend_service.site_backend.id } }