Files
juxtapoandClaude Opus 5 a58a61b98c Pak Jabit: full stack — storefront, Spree customisations, catalogue tooling
A South African men's apparel store on Spree 5.6 + Next.js 15. 160 products
live, three markets, live Malaysian courier rates, customer-owned carts.

What is here:
  storefront/  the Next.js app
  backend/     ONLY the custom Spree code. Spree itself is a prebuilt image
               that mounts no app code, so every file here needs an explicit
               volume entry in docker-compose.yml or it is silently ignored.
  catalogue/   the scripts that loaded and maintain the products
  docs/        manifest, mock registry, napkins

Real .env files are committed deliberately. This lives on the house's private
Gitea, and a clone that runs beats a clone that needs a secrets handover.
LANDMINE.md maps every key, what it opens, and what must be rotated before this
repo goes anywhere less private.

The admin API key used to be a literal in six catalogue scripts. They now read
SPREE_ADMIN_KEY from the environment and refuse to run without it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-15 22:09:55 +08:00

49 lines
1.4 KiB
Python

import json, sys, time, requests
import os as _os
def _spree_admin_key() -> str:
"""The Spree admin API key. Required — never defaulted."""
key = _os.environ.get("SPREE_ADMIN_KEY", "").strip()
if not key:
raise SystemExit(
"SPREE_ADMIN_KEY is not set.\n"
" export SPREE_ADMIN_KEY=<the admin API key>\n"
"It is deliberately not stored in this file — see README.md."
)
return key
API = "https://api.jux-lab.com/api/v3/admin"
KEY = _spree_admin_key()
H = {"X-Spree-Api-Key": KEY, "Content-Type": "application/json"}
products, page = [], 1
while True:
d = requests.get(f"{API}/products?per_page=100&page={page}", headers=H, timeout=60).json()
products += d.get("data", [])
if page >= d.get("meta", {}).get("pages", 1):
break
page += 1
print(f"{len(products)} products")
ok = fail = 0
for i, p in enumerate(products):
vid = p.get("default_variant_id")
if not vid:
fail += 1
continue
r = requests.patch(f"{API}/products/{p['id']}/variants/{vid}", headers=H,
json={"count_on_hand": 100, "track_inventory": False}, timeout=60).json()
if "error" in r:
fail += 1
print(f" FAIL {p['slug'][:40]}: {r['error'].get('message')}")
else:
ok += 1
if (i + 1) % 25 == 0:
print(f" {i+1}/{len(products)}")
time.sleep(0.15)
print(f"Done. ok={ok} fail={fail}")