fix(m3): Option A must-fix #79 #82 #83 #84 #89 #98

- #79 publish SKU/create via WooClient (User-Agent + typed 403 unrecoverable)
- #82 stale_field current_options from live taxonomy + owner labels
- #83 create allowlist/extra-key safe; taxonomy+enum 4xx on create
- #84 health woo_reachable real probe (non-failing); version 0.5.0-m3
- #89 review persists assembled title/short after taxonomy lock; UI applies
- #98 live-edge from synced term; attrs resolved by slug then name
This commit is contained in:
Ty
2026-09-21 12:49:53 -07:00
parent 96a697a50a
commit 0c00d25cc7
7 changed files with 328 additions and 108 deletions
+22 -18
View File
@@ -25,7 +25,7 @@ from starlette.middleware.base import BaseHTTPMiddleware
from .db import get_connection, init_db, probe_images_writable, probe_writable
from . import settings as settings_mod
__version__ = "0.5.0-m3-s1"
__version__ = "0.5.0-m3"
# --- Structured JSON logging (stdlib only) ---
request_id_var: ContextVar[str] = ContextVar("request_id", default="")
@@ -156,35 +156,39 @@ def _error(code: str, message: str, detail: Any = None) -> dict:
@app.get("/api/health")
async def health() -> dict[str, Any]:
"""Health per APP-30/45: non-200 when db or image volume not writable (probe file).
#84: woo_reachable is a real check (HEAD /wp-json/ with a short timeout),
not a hardcoded stub.
#84: woo_reachable is a real authenticated probe (signal only — store outage
does not fail health).
"""
db_ok = probe_writable()
img_ok = probe_images_writable()
if not (db_ok and img_ok):
logger.warning("health: storage not writable", extra={"request_id": request_id_var.get("")})
raise HTTPException(status_code=503, detail="storage not writable")
woo_reachable = False
try:
woo_base = os.environ.get("WOO_BASE_URL", "")
if woo_base:
import urllib.request
req = urllib.request.Request(
woo_base.rstrip("/") + "/wp-json/",
method="GET",
headers={"User-Agent": "SlabUploader/health"},
)
with urllib.request.urlopen(req, timeout=3) as resp:
woo_reachable = resp.status < 500
except Exception:
woo_reachable = False
return {
"status": "ok",
"version": __version__,
"woo_reachable": woo_reachable,
"woo_reachable": _probe_woo_reachable(),
}
def _probe_woo_reachable() -> bool:
"""Cheap non-failing Woo reachability probe for health (SETTINGS-40 style).
Storage health never depends on this result. Missing creds → False.
"""
try:
base, username, enc_password = _woo_credentials()
if not base or not username or not enc_password:
return False
client = WooClient.from_settings(base, username, enc_password)
# One lightweight authenticated call (categories page 1).
code, _h, _b = client._get("/products/categories?per_page=1&page=1")
return code == 200
except Exception:
return False
@app.get("/api/v1/meta")
async def meta() -> dict[str, Any]:
"""Per API.md §3."""