fix(whatsapp,roles): self-heal legacy whatsapp_log schema; map underscore role aliases
CT115 (Mumuni relay #747) — two live-instance defects after PR #12:
1. GET /api/whatsapp/mock-log 500'd with a valid admin token:
'no such column: whatsapp_log.message_text'. The model gained
message_text/wa_message_id/ticket_number (and dropped command) in
4afdc36 with no migration, so legacy DBs keep the (command, ...) shape.
ensure_legacy_schema (startup, app/main.py) now adds the three missing
columns idempotently and backfills legacy command bodies into
message_text before dropping the obsolete NOT NULL command column, so
both the mock-log read path and the ORM write path work on healed DBs.
New producer/consumer regression (tests/test_whatsapp_log_legacy_heal.py)
reproduces the exact OperationalError, then asserts 200 + data.
2. ROLE_ALIASES gap: underscore legacy roles (cs_rep, cs_manager,
fm_dispatcher) were not mapped, so normalize_legacy_user_roles could not
converge rows like user 18 (test@denya.com, role 'cs_rep') and the
frontend stranded them on /tickets. Added the underscore aliases; tests
assert normalize_role('cs_rep') == 'CS Rep' and a cs_rep row converges
and authenticates.
This commit is contained in:
@@ -66,6 +66,13 @@ sign-up UI; users are created/managed by admins only (P0 hardening batch).
|
||||
| POST | `/api/whatsapp/webhook` | `X-Webhook-Secret` header | Inbound message → ticket + log. Fail-closed: 403 when `WHATSAPP_WEBHOOK_SECRET` is unset or the header doesn't match |
|
||||
| GET | `/api/whatsapp/mock-log` | Bearer | Recent webhook submissions (debug; auth required) |
|
||||
|
||||
`whatsapp_log` predates the real Meta webhook (the model gained
|
||||
`message_text`/`wa_message_id`/`ticket_number` and dropped `command` without a
|
||||
migration), so legacy tables keep the old `(command, …)` shape and every ORM
|
||||
read/write 500s. `ensure_legacy_schema` (app/main.py) adds the missing columns
|
||||
and backfills+drops the obsolete NOT NULL `command` column idempotently at
|
||||
startup — do not hand-edit legacy DBs, ship a self-heal there instead.
|
||||
|
||||
### Pages (Sprint 3) — Jinja2 templates at `app/templates/`
|
||||
| Method | Path | Auth | Description |
|
||||
|--------|------|------|-------------|
|
||||
@@ -123,7 +130,9 @@ Frontend: Alpine.js + Tailwind CSS vendored same-origin (no CDN) — see
|
||||
old open register) fail closed at login/JWT and can never be recreated via the
|
||||
API (422). Startup self-heals (lifespan in `app/main.py`, helpers in
|
||||
`app/services/seed.py`) converge legacy rows: `normalize_legacy_user_roles`
|
||||
maps unambiguous alias nicknames onto canonical roles, and
|
||||
maps unambiguous alias nicknames onto canonical roles (space and underscore
|
||||
forms alike — `cs rep`/`cs_rep` → `CS Rep`, `fm dispatcher`/`fm_dispatcher`,
|
||||
`cs manager`/`cs_manager`), and
|
||||
`normalize_legacy_user_emails` lowercases stored emails — login and the admin
|
||||
create-user duplicate check both compare on the lowercased form, so pre-P0
|
||||
mixed-case emails are never silently locked out.
|
||||
|
||||
Reference in New Issue
Block a user