feat(enterprise): align account limits API

This commit is contained in:
2026-06-07 13:46:17 +08:00
parent c28eb010c6
commit 64f36ad642
11 changed files with 610 additions and 17 deletions
+8 -6
View File
@@ -49,16 +49,16 @@ Hermes task landing checklist:
## Current Baseline
- Current tracking checkpoint: 2026-06-07 P6.1 webhook placeholder fallback burn-down, prepared as `fix(webhooks): replace parity stubs`.
- Latest implementation checkpoint: this checkpoint, prepared as `fix(webhooks): replace parity stubs`.
- Latest documentation/tooling checkpoint: this tracker update records removal of `chatwootParityStub`, explicit webhook nil-handler fallback behavior, and the refreshed Phase 6 placeholder audit.
- Current tracking checkpoint: 2026-06-07 P3.102 enterprise account limits and billing-route parity, prepared as `feat(enterprise): align account limits API`.
- Latest implementation checkpoint: this checkpoint, prepared as `feat(enterprise): align account limits API`.
- Latest documentation/tooling checkpoint: this tracker update records the enterprise account API route expansion, Chatwoot account-limit payload shape, deletion toggle side effects, and regenerated route parity artifacts.
- Plan landing status: complete for the current known Hermes plans and user-confirmed scope. Future work should update this file directly instead of opening a parallel tracker.
- Worktree status at this implementation checkpoint: the remaining `chatwootParityStub` webhook nil-handler fallbacks are replaced with explicit `503 webhook provider unavailable` responses, and the unused placeholder helpers are removed. This keeps real provider handlers untouched when wired, prevents any registered route from returning a placeholder/not-implemented body when a handler is missing, and refreshes the Phase 6 placeholder audit. This retains P3.101 WhatsApp call route-parameter parity, P3.100 dashboard app route-parameter parity, P3.99 conversation destroy async parity, P3.98 conversation transcript delivery parity, P3.97 conversation typing event parity, and prior checkpoints. Live API/browser/enterprise smoke still needs the full PostgreSQL/Redis/Meilisearch/GoChat/Vite/Chrome stack.
- Worktree status at this implementation checkpoint: the reused dashboard enterprise account client routes from `reference/chatwoot/app/javascript/dashboard/api/enterprise/account.js` and `routes.rb:523-527` are registered under `/enterprise/api/v1/accounts/:account_id/*`. `limits` returns Chatwoot-shaped `{ id, limits }` usage data, `toggle_deletion` writes/removes Chatwoot deletion custom attributes, `subscription` records the local customer-creation guard, and checkout/top-up endpoints return explicit billing-provider errors instead of missing routes. This retains P6.1 webhook placeholder burn-down, P3.101 WhatsApp call route-parameter parity, P3.100 dashboard app route-parameter parity, and prior checkpoints. Live API/browser/enterprise smoke still needs the full PostgreSQL/Redis/Meilisearch/GoChat/Vite/Chrome stack.
- Next executable implementation checkpoint: continue Phase 2/3 drift audit for the next reused-frontend mismatch, or run B12 live smoke when the full PostgreSQL/Redis/Meilisearch/GoChat/Vite/Chrome stack is available. Re-run Phase 6 placeholder audit after future route/smoke changes.
- `go test ./...` passes when run outside the restricted socket sandbox for the latest implementation baseline; the latest docs/tooling checkpoint verified `scripts/parity_frontend_smoke.sh --check` with workspace-local temp/cache dirs after `/tmp` was full.
- Route dump succeeds with `967` registered routes after WhatsApp call route-parameter tracking.
- Route dump succeeds with `972` registered routes after enterprise account route tracking.
- Route parity artifacts now exist under `docs/parity/` and are generated by `cmd/route_parity`.
- Tracked frontend-critical route audit covers 439 Chatwoot routes: 430 exact, 0 method-compatible, 9 parameter-compatible, 0 missing. The 9 parameter-compatible routes are Gin-internal parameter-name differences for nested AgentCapacityPolicy users/inbox limits plus the public article `.md`/`.png` suffixes served through the same external article route dispatcher.
- Tracked frontend-critical route audit covers 444 Chatwoot routes: 435 exact, 0 method-compatible, 9 parameter-compatible, 0 missing. The 9 parameter-compatible routes are Gin-internal parameter-name differences for nested AgentCapacityPolicy users/inbox limits plus the public article `.md`/`.png` suffixes served through the same external article route dispatcher.
- `/api/v1/widget` stubs are burned down, public inbox/contact/conversation/message core flows are backed by real handlers, and `chatwootParityStub` no longer exists in product code.
- Handler test stability fixes are committed into the baseline before feature parity work continues.
- `.codegraph/` is generated indexing output and is not part of tracked product code.
@@ -156,6 +156,7 @@ This table is the shortest authoritative handoff view. If an older lower section
| Priority | Workstream | Current state | Next checkpoint | Commit close rule |
| --- | --- | --- | --- | --- |
| 0 | P3.102 enterprise account limits and billing routes | Implemented for reused enterprise account frontend calls: `GET/POST /enterprise/api/v1/accounts/:account_id/{limits,checkout,subscription,toggle_deletion,topup_checkout}` are now registered and tracked from `routes.rb:523-527`; `limits` returns Chatwoot-shaped usage data for agents, Captain documents/responses, and default-plan conversation/non-web-inbox counts; `toggle_deletion` mutates `marked_for_deletion_at` and `marked_for_deletion_reason`; `subscription` persists the `is_creating_customer` guard when no Stripe customer exists; checkout/top-up return explicit billing-provider errors instead of missing routes. | Keep in Review; reopen from B12 enterprise account smoke or fresh reference evidence for actual Stripe session creation, cloud-env gating, plan-config defaults, or account deletion notification/cancellation jobs beyond the local persisted boundary. | Focused EnterpriseAccountHandler tests passed; route dump/parity artifacts regenerated to `TOTAL: 972` and `435 exact, 0 method-compatible, 9 parameter-compatible, 0 missing out of 444`; full `go test ./...` passed outside the restricted socket sandbox; `git diff --check` passed. |
| 0 | P6.1 webhook placeholder fallback burn-down | Implemented for Phase 6 placeholder cleanup: `chatwootParityStub` and the unused webhook placeholder helper are removed from product code. Public webhook nil-handler guards now return explicit `503 { error: "webhook provider unavailable", message: "webhook handler is not configured" }` responses instead of `501 not implemented` placeholder bodies, while wired provider handlers still own real Telegram, WhatsApp, TikTok, LINE, Twilio, Twitter, Instagram, and Shopify behavior. | Keep in Review; reopen from placeholder audit or webhook smoke if a frontend/provider-reachable route returns placeholder/not-implemented content or if a provider handler is missing from normal bootstrap. | Focused router tests cover route boot and nil-handler fallback body/status; placeholder audit refreshed; full `go test ./...` passed outside the restricted socket sandbox; `git diff --check` passed. No route artifacts change. |
| 0 | P3.101 WhatsApp call route parameter parity | Implemented for reused WhatsApp call controls: account-level show/accept/reject/terminate/upload-recording routes now register Chatwoot's `:id` parameter name from `routes.rb:237-242`, while the handler still accepts legacy `:call_id` mounts. Route parity improves from `425 exact / 14 parameter-compatible` to `430 exact / 9 parameter-compatible` with no missing critical routes. AgentCapacity nested route names stay parameter-compatible in the main Gin router because Gin cannot register `/:id` policy members and `/:agent_capacity_policy_id` nested siblings under the same prefix without a wildcard conflict; the nested handlers now tolerate both parameter-name families. | Keep in Review; reopen from B12 WhatsApp calling smoke or fresh reference evidence for route/action payload drift beyond the inspected enterprise controller/frontend call API contract. | Focused WhatsAppCallHandler, AgentCapacityHandler, and router tests passed; route dump/parity artifacts regenerated; full `go test ./...` passed outside the restricted socket sandbox after retrying the known `internal/worker` SQLite in-memory flake; `git diff --check` passed. |
| 0 | P3.100 dashboard app route parameter parity | Implemented for reused dashboard app settings routes: GoChat now registers standard dashboard app member routes with Chatwoot's `:id` parameter name for `show/update/destroy`, keeps `PATCH` and `PUT`, and preserves legacy handler compatibility for local focused tests and widget extension routes. Route parity improves from `421 exact / 18 parameter-compatible` to `425 exact / 14 parameter-compatible` with no missing critical routes. | Keep in Review; reopen from B12 dashboard app settings smoke or fresh reference evidence for serializer/request-permit drift beyond the inspected controller/Jbuilder/frontend store contract. | Focused DashboardAppHandler and router tests passed; route dump/parity artifacts regenerated; full `go test ./...` passed outside the restricted socket sandbox; `git diff --check` passed. |
@@ -2786,3 +2787,4 @@ Verification milestone gates:
- 2026-06-07: P3.100 dashboard app route-parameter checkpoint prepared as `fix(routes): align dashboard app ids`; audited Chatwoot `routes.rb:130`, `DashboardAppsController`, dashboard app Jbuilder views, and reused dashboard `dashboardApps.js` API/store calls. GoChat now registers dashboard app member CRUD routes with Chatwoot's `:id` parameter name, keeps `PATCH` and `PUT`, and preserves legacy local `:dashboard_app_id` parsing for focused tests and GoChat-only widget extensions. Focused DashboardAppHandler and router tests passed; route dump/parity regenerated to `TOTAL: 967` and `425 exact, 0 method-compatible, 14 parameter-compatible, 0 missing out of 439`; full `go test ./...` passed outside the restricted socket sandbox; `git diff --check` passed.
- 2026-06-07: P3.101 WhatsApp call route-parameter checkpoint prepared as `fix(routes): align whatsapp call ids`; audited Chatwoot `routes.rb:237-242` and the reused account WhatsApp call route family. GoChat now registers account-level WhatsApp call show/action routes with Chatwoot's `:id` parameter name, preserves legacy `:call_id` handler compatibility, and makes AgentCapacity nested handlers accept both Chatwoot `:agent_capacity_policy_id`/`:id` and local Gin-compatible `:id`/`:user_id`/`:limit_id` parameter names. The main router keeps AgentCapacity nested route names parameter-compatible because Gin rejects wildcard-name changes below the existing policy `/:id` member route. Focused WhatsAppCallHandler, AgentCapacityHandler, and router tests passed; route dump/parity regenerated to `TOTAL: 967` and `430 exact, 0 method-compatible, 9 parameter-compatible, 0 missing out of 439`; full `go test ./...` passed outside the restricted socket sandbox after retrying the known `internal/worker` SQLite in-memory flake; `git diff --check` passed.
- 2026-06-07: P6.1 webhook placeholder fallback checkpoint prepared as `fix(webhooks): replace parity stubs`; refreshed the Phase 6 placeholder audit and burned down the remaining `chatwootParityStub` nil-handler fallbacks in `internal/router/router.go`. Public webhook routes now return explicit `503 webhook provider unavailable` JSON when a provider handler is not configured instead of `501 not implemented` placeholder bodies, and the unused placeholder helper is removed. Focused router tests cover boot and nil-handler fallback behavior; placeholder audit shows no `chatwootParityStub` product-code matches; full `go test ./...` passed outside the restricted socket sandbox; `git diff --check` passed. No route artifacts change.
- 2026-06-07: P3.102 enterprise account limits checkpoint prepared as `feat(enterprise): align account limits API`; audited Chatwoot enterprise `AccountsController#limits/#toggle_deletion/#subscription/#checkout/#topup_checkout`, `BillingHelper`, `Enterprise::Account::PlanUsageAndLimits`, reused dashboard `api/enterprise/account.js`, and `routes.rb:523-527`. GoChat now registers the enterprise account route family under `/enterprise/api/v1/accounts/:account_id`, returns Chatwoot-shaped account limit payloads, persists scheduled deletion custom attributes, records subscription customer-creation guards, and exposes explicit local billing-provider errors for checkout/top-up paths. Focused EnterpriseAccountHandler tests passed; route dump/parity regenerated to `TOTAL: 972` and `435 exact, 0 method-compatible, 9 parameter-compatible, 0 missing out of 444`; full `go test ./...` passed outside the restricted socket sandbox; `git diff --check` passed.
+6 -1
View File
@@ -444,6 +444,7 @@ GET /app
GET /app/*params
GET /auth/validate_token
GET /cable
GET /enterprise/api/v1/accounts/:account_id/limits
GET /google/callback
GET /hc/:slug
GET /hc/:slug/:locale
@@ -834,6 +835,10 @@ POST /api/v2/accounts/
POST /auth/confirmation
POST /auth/password
POST /auth/sign_in
POST /enterprise/api/v1/accounts/:account_id/checkout
POST /enterprise/api/v1/accounts/:account_id/subscription
POST /enterprise/api/v1/accounts/:account_id/toggle_deletion
POST /enterprise/api/v1/accounts/:account_id/topup_checkout
POST /platform/api/v1/accounts
POST /platform/api/v1/accounts/:account_id/account_users
POST /platform/api/v1/agent_bots
@@ -965,4 +970,4 @@ PUT /public/api/v1/csat_survey/:id
PUT /public/api/v1/inboxes/:inbox_id/contacts/:contact_id
PUT /public/api/v1/inboxes/:inbox_id/contacts/:contact_id/conversations/:conversation_id/messages/:message_id
PUT /widget/direct_uploads/:upload_uuid
TOTAL: 967
TOTAL: 972
+6 -1
View File
@@ -7,7 +7,7 @@ Generated from:
This report covers tracked frontend-critical Chatwoot routes from `reference/chatwoot/config/routes.rb`, including API v1 account routes, Captain/Copilot, assignment policies, widget/public APIs, and API v2 reports. Ruby is not installed in the workspace, so Chatwoot routes are sourced from static route declarations instead of `bin/rails routes`.
Summary: 430 exact, 0 method-compatible, 9 parameter-compatible, 0 missing out of 439 tracked critical routes.
Summary: 435 exact, 0 method-compatible, 9 parameter-compatible, 0 missing out of 444 tracked critical routes.
## Missing Critical Routes
@@ -243,6 +243,7 @@ These routes exist with equivalent method and path shape but different parameter
| GET | `/api/v2/accounts/:account_id/year_in_review` | `/api/v2/accounts/:account_id/year_in_review` | `api/v2/accounts/year_in_reviews#show` | `routes.rb:505` | exact |
| GET | `/app` | `/app` | `dashboard#index` | `routes.rb:19` | exact |
| GET | `/app/*params` | `/app/*params` | `dashboard#index` | `routes.rb:20` | exact |
| GET | `/enterprise/api/v1/accounts/:account_id/limits` | `/enterprise/api/v1/accounts/:account_id/limits` | `enterprise/api/v1/accounts#limits` | `routes.rb:525` | exact |
| GET | `/google/callback` | `/google/callback` | `google/callbacks#show` | `routes.rb:650` | exact |
| GET | `/hc/:slug` | `/hc/:slug` | `public/api/v1/portals#show` | `routes.rb:590` | exact |
| GET | `/hc/:slug/:locale` | `/hc/:slug/:locale` | `public/api/v1/portals#show` | `routes.rb:592` | exact |
@@ -420,6 +421,10 @@ These routes exist with equivalent method and path shape but different parameter
| POST | `/api/v1/widget/labels` | `/api/v1/widget/labels` | `api/v1/widget/labels#create` | `routes.rb:464` | exact |
| POST | `/api/v1/widget/messages` | `/api/v1/widget/messages` | `api/v1/widget/messages#create` | `routes.rb:447` | exact |
| POST | `/api/v2/accounts/` | `/api/v2/accounts/` | `api/v2/accounts#create` | `routes.rb:478` | exact |
| POST | `/enterprise/api/v1/accounts/:account_id/checkout` | `/enterprise/api/v1/accounts/:account_id/checkout` | `enterprise/api/v1/accounts#checkout` | `routes.rb:523` | exact |
| POST | `/enterprise/api/v1/accounts/:account_id/subscription` | `/enterprise/api/v1/accounts/:account_id/subscription` | `enterprise/api/v1/accounts#subscription` | `routes.rb:524` | exact |
| POST | `/enterprise/api/v1/accounts/:account_id/toggle_deletion` | `/enterprise/api/v1/accounts/:account_id/toggle_deletion` | `enterprise/api/v1/accounts#toggle_deletion` | `routes.rb:526` | exact |
| POST | `/enterprise/api/v1/accounts/:account_id/topup_checkout` | `/enterprise/api/v1/accounts/:account_id/topup_checkout` | `enterprise/api/v1/accounts#topup_checkout` | `routes.rb:527` | exact |
| POST | `/public/api/v1/inboxes/:inbox_id/contacts` | `/public/api/v1/inboxes/:inbox_id/contacts` | `public/api/v1/inboxes/contacts#create` | `routes.rb:572` | exact |
| POST | `/public/api/v1/inboxes/:inbox_id/contacts/:contact_id/conversations` | `/public/api/v1/inboxes/:inbox_id/contacts/:contact_id/conversations` | `public/api/v1/inboxes/conversations#create` | `routes.rb:573` | exact |
| POST | `/public/api/v1/inboxes/:inbox_id/contacts/:contact_id/conversations/:conversation_id/messages` | `/public/api/v1/inboxes/:inbox_id/contacts/:contact_id/conversations/:conversation_id/messages` | `public/api/v1/inboxes/messages#create` | `routes.rb:580` | exact |