1.7 KiB
PieCed Portal — Billing Phase 1 patch (suspend-via-admin fix)
Single-file fix on top of the Phase 1 v2 drop.
What it fixes
The admin panel's suspend/resume button hits
/api/admin/tenants/[name]/suspend (a different route from the
customer-side /api/tenants/[name]/suspend). The v2 drop only
hooked the customer route — admin suspends were going to K8s
without producing a row in tenant_suspension_events.
This patch adds the same recordSuspensionEvent hook to the
admin route. No other code paths affected; no schema changes.
Files
src/app/api/admin/tenants/[name]/suspend/route.ts MODIFIED
Deploy
Extract over your pieced-portal/ tree, rebuild, redeploy as
usual. After the new image is running, verify:
-
Suspend any test tenant from the
/adminpanel. -
Check the events table:
kubectl -n pieced-system exec -it portal-db-1 -- psql -U postgres -d portal -c \ "SELECT * FROM tenant_suspension_events ORDER BY id DESC LIMIT 5;"Expect a fresh
suspendedrow for the tenant you just toggled. -
Resume → expect a
resumedrow.
Why I missed this
Both routes share the same shape (PATCH/POST that sets
spec.suspend), but they differ on:
- URL path (
/api/admin/tenants/...vs/api/tenants/...) - Method (POST vs PATCH)
- Authorization (platform-only vs owner+platform)
- Caller (admin panel vs customer cancel button)
When I grepped for the suspend hook target I matched on the
customer endpoint and didn't audit cross-cutting admin
duplicates. I've since checked every site that calls
patchTenantSpec, createTenant, or deleteTenant — this was
the only missed billing-relevant one. Other patchTenantSpec
sites are confirmed non-billing (openClawImage, channelUsers).