Initial commit of Descrybe v2 without local scratch artifacts.
Drop one-shot tmp/axe scripts and agent i18n scratch so the Gitea tree is deployable.
This commit is contained in:
@@ -0,0 +1,310 @@
|
||||
# Full app QA report (Descrybe v2)
|
||||
|
||||
**Date:** 2026-08-04
|
||||
**Repo:** `f:/laragon/www/_MY/descrybe-v2`
|
||||
**Wave:** 20 parallel agents (credits, analytics, Stripe, tutorial, SEO copy, Woo, E2E, Free tier, settings, products, catalog, admin/API, security, dashboard, processing, exports, nav) + final reconcile
|
||||
**Build gates (reconcile):** `go build ./...` → **PASS** · `npm run check` (apps/web) → **0 errors / 0 warnings**
|
||||
|
||||
---
|
||||
|
||||
## Executive summary
|
||||
|
||||
The 20-agent QA wave is complete. Descrybe v2 is green to demo locally: credits and analytics now read the **live wallet** (no more stale cycle inflation), Stripe Checkout works in **mock** (`STRIPE_MOCK=true`) and is ready for real keys, the in-app tutorial is forced-on-actions but skippable, Woo has a seed path without a live shop, and both catalog and marketing E2E suites passed.
|
||||
|
||||
**Local Demo Co is back on Enterprise** after mock Stripe checkout temporarily assigned a paid self-serve plan — restored to ~1M AI credits / Unlimited packaging for continued demo use.
|
||||
|
||||
| You want… | Status |
|
||||
|-----------|--------|
|
||||
| Demo the product end-to-end | **Ready** — login below |
|
||||
| Buy a plan in prod | Set Stripe env (see § Stripe) |
|
||||
| Connect a real Woo store | UI `/woocommerce` or `WOO_*` env (see § Woo) |
|
||||
| Try Woo audiences without WP | `make seed-woo` / `seed-woo-demo` |
|
||||
|
||||
**Still not production-cutover:** FTP feed sync, daily credit ledger, live Stripe/Woo untested in this wave, and cutover gates in [cutover.md](cutover.md).
|
||||
|
||||
---
|
||||
|
||||
## Demo login (use this)
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| Email | `demo@descrybe.local` (alias `demo@descrybe.test` also works) |
|
||||
| Password | `DemoPass123!` |
|
||||
| Company | **Local Demo Co** (`ee246275-dec0-4446-9e83-58d0c16c258a`) |
|
||||
| Plan | **Enterprise** · ~1,000,000 AI credits · `can_use_ai=true` (restored after mock Stripe) |
|
||||
| Platform admin | yes |
|
||||
| Web | http://127.0.0.1:5174/login |
|
||||
| API | http://127.0.0.1:8080 |
|
||||
| Docs | http://127.0.0.1:5174/docs |
|
||||
| Demo API key | `dk_demo_local_descrybe_test_key_v1` |
|
||||
|
||||
Do **not** use this account or key in production. Details: [demo-user.md](demo-user.md).
|
||||
|
||||
---
|
||||
|
||||
## Executive matrix
|
||||
|
||||
| Area | Verdict | Evidence doc |
|
||||
|------|---------|--------------|
|
||||
| Credits / billing | **PASS** (bugs fixed) | [billing-credits-audit.md](billing-credits-audit.md) |
|
||||
| Analytics / usage | **PASS** (bugs fixed) | [analytics-audit.md](analytics-audit.md) |
|
||||
| Stripe Checkout / portal / webhooks | **PASS** (mock + real path; demo Enterprise restored after mock) | [stripe-setup.md](stripe-setup.md) |
|
||||
| Free tier gates | **PASS** | [free-tier-verify.md](free-tier-verify.md) · [free-tier.md](free-tier.md) |
|
||||
| Tutorial (forced + skippable) | **PASS** | [tutorial.md](tutorial.md) |
|
||||
| SEO / marketing copy | **PASS** | sibling SEO pass + `/pricing` polish |
|
||||
| WooCommerce | **PASS** (seed path; live optional) | [woocommerce-demo.md](woocommerce-demo.md) |
|
||||
| E2E feeds → process → export | **PASS** | [e2e-feeds-process-export.md](e2e-feeds-process-export.md) |
|
||||
| E2E marketing (brand/SEO/campaign/email) | **PASS** | [e2e-marketing.md](e2e-marketing.md) |
|
||||
| Processing UI | **PASS** | [e2e-processing.md](e2e-processing.md) |
|
||||
| Admin / health / public API | **PASS** 20/20 | [api-surface-smoke.md](api-surface-smoke.md) |
|
||||
| Security (Stripe webhook / SSRF) | **PASS** (hardened) | [security-notes.md](security-notes.md) |
|
||||
| Settings / API keys / invites | **PASS** | sibling settings QA |
|
||||
| Products / categories / attributes | **PASS** (UX fixes) | siblings |
|
||||
| Export public URLs | **PASS** | sibling export QA |
|
||||
| AI (Green Chat LAN) | **PASS** when `:8767` open | [ai-full-smoke.md](ai-full-smoke.md) · [green-chat-smoke.md](green-chat-smoke.md) |
|
||||
|
||||
---
|
||||
|
||||
## 1. Credits & billing
|
||||
|
||||
**Verdict:** Demo Enterprise wallet and Billing UI agree: **1,000,000 remaining / 0 used**. Free grants **0** AI credits.
|
||||
|
||||
### What was wrong
|
||||
|
||||
1. `/api/billing/usage` preferred stale `billing_cycles` rows → showed e.g. **22 credits used** while wallet was 1M/0.
|
||||
2. Campaign AI swallowed `ConsumeCredits` errors (silent free AI).
|
||||
3. SEO AI only checked `CanUseAI` (true on paid with empty wallet).
|
||||
4. Billing page fake date-range + false “Out of credits” on Free.
|
||||
|
||||
### Fixes
|
||||
|
||||
- `UsageSummary` always uses live `credit_balances` for credits; range filters products/tokens only.
|
||||
- `AssignPlan` closes open cycles and opens a fresh cycle; `ConsumeCredits` updates only open cycles.
|
||||
- Campaign/SEO require `RemainingCredits ≥ 1`; debit after AI; map insufficient → **402**.
|
||||
- Billing UI: real `?range=`, Free info banner, used/total + Enterprise “Unlimited” label.
|
||||
|
||||
### Smoke
|
||||
|
||||
```text
|
||||
CREDITS plan=Enterprise total=1000000 used=0 rem=1000000
|
||||
USAGE matches /auth/me
|
||||
AFTER_FREE → rem=0 can_use_ai=false
|
||||
AFTER_ENT → rem=1000000
|
||||
```
|
||||
|
||||
Full write-up: [billing-credits-audit.md](billing-credits-audit.md).
|
||||
|
||||
---
|
||||
|
||||
## 2. Analytics & usage
|
||||
|
||||
**Verdict:** Tenant + admin meters match live Postgres for Local Demo Co.
|
||||
|
||||
| Meter | API | DB |
|
||||
|-------|-----|-----|
|
||||
| Wallet used / total | 0 / 1_000_000 | match |
|
||||
| Products (all) | ~4326 | match |
|
||||
| Input / export feeds | 12 / 5 | match |
|
||||
| Plan cycle | Aug → Sep 2026 | `company_plans` |
|
||||
|
||||
Admin analytics now includes feed counts and real `ai_provider_mode` rollups (after API rebuild). Details: [analytics-audit.md](analytics-audit.md).
|
||||
|
||||
---
|
||||
|
||||
## 3. Stripe (purchase path)
|
||||
|
||||
**Verdict:** Checkout + Customer Portal + signed webhooks implemented. Local works in **mock** without a Stripe account.
|
||||
|
||||
### Configure for real
|
||||
|
||||
1. Set in **root** `.env` (never commit secrets; placeholders in `.env.example`):
|
||||
|
||||
| Variable | Purpose |
|
||||
|----------|---------|
|
||||
| `STRIPE_SECRET_KEY` | `sk_test_…` / `sk_live_…` |
|
||||
| `STRIPE_WEBHOOK_SECRET` | `whsec_…` |
|
||||
| `STRIPE_MOCK` | `false` for live |
|
||||
| `STRIPE_PRICE_STARTER_MONTHLY` / `_YEARLY` | Price IDs |
|
||||
| `STRIPE_PRICE_GROWTH_MONTHLY` / `_YEARLY` | Price IDs |
|
||||
| `STRIPE_PRICE_BUSINESS_MONTHLY` / `_YEARLY` | Price IDs |
|
||||
| `WEB_ORIGIN` | e.g. `https://app.example.com` (Checkout return URLs) |
|
||||
|
||||
2. Stripe Dashboard: Products + Prices for Starter / Growth / Business; enable Customer Portal.
|
||||
3. Webhook → `https://<api-host>/api/webhooks/stripe`
|
||||
Events: `checkout.session.completed`, `customer.subscription.created|updated|deleted`.
|
||||
4. Local: `stripe listen --forward-to localhost:8080/api/webhooks/stripe` → paste `whsec_…`.
|
||||
5. Apply migration `016_stripe_billing.sql` (`pwsh -File scripts/migrate.ps1`), restart API.
|
||||
|
||||
### Mock local QA
|
||||
|
||||
`STRIPE_MOCK=true` → `POST /api/billing/checkout` assigns plan + credits and returns `mock:true`. Empty secret alone no longer free-upgrades (security harden).
|
||||
|
||||
**Note:** Mock checkout on Local Demo Co temporarily moved the tenant off Enterprise (e.g. to Starter). Enterprise was **restored** afterward so demo stays on ~1M credits / Unlimited. Re-run `go run ./cmd/seed-demo` (or admin AssignPlan Enterprise) if mock Checkout is used again on the demo company.
|
||||
|
||||
Enterprise stays sales-led (no self-serve price). UI: `/plans`, `/billing`. Full guide: [stripe-setup.md](stripe-setup.md).
|
||||
|
||||
---
|
||||
|
||||
## 4. WooCommerce
|
||||
|
||||
**Verdict:** Product push + orders/reviews pull + campaign audiences work. Live shop optional; **seed path** covers demo without WordPress.
|
||||
|
||||
### Configure for real
|
||||
|
||||
1. WooCommerce → Settings → Advanced → REST API → Read/Write key.
|
||||
2. Descrybe `/woocommerce` → enable sync → Store URL + `ck_…` / `cs_…` → Save → Test Connection.
|
||||
3. Queue product sync, then Orders / Reviews; keep **worker** running.
|
||||
4. Optional env (or UI only): `WOO_STORE_URL`, `WOO_CONSUMER_KEY`, `WOO_CONSUMER_SECRET`
|
||||
(aliases `WOOCOMMERCE_*` also work). Encrypt at rest needs `CREDENTIALS_ENCRYPTION_KEY`.
|
||||
5. SSRF: `http` allowed only for localhost/loopback; remote shops need https (or tunnel).
|
||||
|
||||
### No live shop?
|
||||
|
||||
```powershell
|
||||
cd apps/api
|
||||
$env:DATABASE_URL = "postgres://descrybe:descrybe@localhost:5433/descrybe?sslmode=disable"
|
||||
go run ./cmd/seed-woo-demo -postgres $env:DATABASE_URL -company "Local Demo Co"
|
||||
# or: make seed-woo
|
||||
```
|
||||
|
||||
Seeds Demo Electronics products, 4 orders, 3 reviews, draft “purchased” campaign. Details: [woocommerce-demo.md](woocommerce-demo.md).
|
||||
|
||||
---
|
||||
|
||||
## 5. Tutorial
|
||||
|
||||
**Verdict:** Forced action tour with Skip / Pause / Resume / Restart. No free Continue on action steps.
|
||||
|
||||
- Progress: `localStorage` key `descrybe.tutorial.v2`
|
||||
- 15 steps: welcome → standard fields → feeds map/auto-map/save/sync-sample → products → export → campaigns → done
|
||||
- Advance only on real clicks (`data-tour`) or successful `tutorial.reportAction(…)`
|
||||
- Header + dashboard: Start / Resume / Restart
|
||||
|
||||
Manual checklist: [tutorial.md](tutorial.md).
|
||||
|
||||
---
|
||||
|
||||
## 6. E2E results
|
||||
|
||||
### Feeds → map → process → export — **PASS**
|
||||
|
||||
| Check | Result |
|
||||
|-------|--------|
|
||||
| 12 input feeds mapped | PASS |
|
||||
| Process job 5/5 steps | PASS (worker required) |
|
||||
| Export XML/CSV generate | PASS (~4322 products) |
|
||||
| Public token downloads | PASS |
|
||||
| Mapping PUT nested `{key,mapping}` | Fixed this wave |
|
||||
|
||||
FTP/FTPS supplier sync still **400** (not supported). Migrated data already present so process/export work without re-sync. Details: [e2e-feeds-process-export.md](e2e-feeds-process-export.md).
|
||||
|
||||
### Marketing (brand / SEO / campaign / email / calendar) — **PASS** (14/14)
|
||||
|
||||
Against Green Chat `overloaded-local` @ `192.168.50.181:8767`:
|
||||
|
||||
| Step | Result |
|
||||
|------|--------|
|
||||
| Brand kit PUT/GET + tips | PASS |
|
||||
| SEO `mode=ai` | PASS (~1s, brand terms, credits charged) |
|
||||
| Campaign `mode=ai` + template | PASS |
|
||||
| Email / campaign `dry_run` | PASS |
|
||||
| Calendar prepare Black Friday | PASS (path fix: `/api/marketing/calendar/prepare`) |
|
||||
|
||||
Details: [e2e-marketing.md](e2e-marketing.md).
|
||||
|
||||
### Processing UI — **PASS**
|
||||
|
||||
Create → cancel (steps marked cancelled) → enhance_only via Green Chat (~1s). `/tasks` → 307 `/processing`. Details: [e2e-processing.md](e2e-processing.md).
|
||||
|
||||
### Free tier (while demo is Enterprise) — **PASS**
|
||||
|
||||
New register → Free / 0 credits / `can_use_ai=false`; AI-only jobs → **402**; normalize/full still runs with AI/EPREL skipped. Demo remains Enterprise. Details: [free-tier-verify.md](free-tier-verify.md).
|
||||
|
||||
### Admin / health / public API — **PASS** 20/20
|
||||
|
||||
Platform admin routes, `/healthz` `/readyz`, RapiDoc docs, OpenAPI, v1 CRUD smoke + 401 without key. Details: [api-surface-smoke.md](api-surface-smoke.md).
|
||||
|
||||
---
|
||||
|
||||
## 7. Other sibling fixes (this wave)
|
||||
|
||||
| Area | Outcome |
|
||||
|------|---------|
|
||||
| **Security** | Stripe webhook always verified when `STRIPE_WEBHOOK_SECRET` set; mock purchases need explicit `STRIPE_MOCK`; generic webhook errors; SSRF notes in [security-notes.md](security-notes.md) |
|
||||
| **Billing / Plans UI** | Enterprise shows Unlimited; Checkout CTAs; no false Free out-of-credits banner |
|
||||
| **Dashboard** | Clear CTAs (tutorial / process / feeds / BF); clickable credit widgets; dead upload section removed |
|
||||
| **SEO / copy** | Plain ecommerce voice; SeoHead on privacy/terms/features; clearer empty states |
|
||||
| **Products** | Search includes processed/raw names; CSV ingest via Feeds only; server-side sort |
|
||||
| **Categories / attributes** | Attributes default `roots=1`; formula 404 UX fixed |
|
||||
| **Settings** | API keys CRUD, invites, email link; Copy Key no longer copies useless prefix |
|
||||
| **Export feeds** | Public routes mounted before session auth (401→404/405); absolute public URLs in UI |
|
||||
| **Nav / a11y / tutorial targets** | `data-tour` preserved for tour |
|
||||
|
||||
---
|
||||
|
||||
## 8. Known gaps
|
||||
|
||||
| Gap | Notes |
|
||||
|-----|-------|
|
||||
| No daily **credit** ledger | Usage range filters products/tokens only; credit burn is wallet-level |
|
||||
| FTP/FTPS feed sync | Not supported; sync returns 400 for those supplier URLs |
|
||||
| Stripe live untested in this wave | Mock + unit/integration coverage; real Checkout needs keys above |
|
||||
| Live Woo optional | Seed covers audiences; live sync needs a real store + worker |
|
||||
| Email delivery | Prefer `force_dry_run` / `EMAIL_DRY_RUN`; stub SMTP test fails without a real listener |
|
||||
| Cutover / production | Staging data OK; production cutover still blocked — see [cutover.md](cutover.md) · [go-live-checklist.md](go-live-checklist.md) |
|
||||
| Migrated tenants | May hold legacy wallets; entitlements treat empty plan as Free-with-leftover-credits |
|
||||
| Campaign/SEO charge after LLM | Wallet gated first; mid-flight race can still burn tokens then fail debit |
|
||||
| Structured-descriptions / vector-categories | UI shells exist; backend still partial ([status-and-gaps.md](status-and-gaps.md)) |
|
||||
| CI | Makefile + unit tests; no GitHub Actions yet |
|
||||
|
||||
---
|
||||
|
||||
## 9. How to re-verify locally
|
||||
|
||||
```powershell
|
||||
# Postgres
|
||||
cd f:\laragon\www\_MY\descrybe-v2
|
||||
docker compose up -d # :5433
|
||||
|
||||
# Build gates
|
||||
cd apps\api; go build ./...
|
||||
cd ..\web; npm run check
|
||||
|
||||
# Runtime (separate terminals)
|
||||
# scripts\run-api.ps1 → API :8080
|
||||
# make worker / go run ./cmd/worker
|
||||
# make web → :5174
|
||||
|
||||
# Login
|
||||
# demo@descrybe.local / DemoPass123!
|
||||
```
|
||||
|
||||
Spot-check:
|
||||
|
||||
1. `/billing` → 1M remaining, usage matches wallet
|
||||
2. `/plans` → Upgrade (mock Checkout if `STRIPE_MOCK=true`)
|
||||
3. Start tutorial → action steps have no Continue
|
||||
4. `/feeds` → map → `/processing` → `/export-feeds` generate
|
||||
5. `/campaigns` + `/seo` AI (needs Green Chat + `OPENAI_*`)
|
||||
6. `/woocommerce` or `make seed-woo` → orders/reviews + audience
|
||||
|
||||
---
|
||||
|
||||
## 10. Related docs index
|
||||
|
||||
| Doc | Topic |
|
||||
|-----|-------|
|
||||
| [billing-credits-audit.md](billing-credits-audit.md) | Credits correctness |
|
||||
| [analytics-audit.md](analytics-audit.md) | Usage / admin analytics |
|
||||
| [stripe-setup.md](stripe-setup.md) | Stripe real + mock |
|
||||
| [woocommerce-demo.md](woocommerce-demo.md) | Woo live + seed |
|
||||
| [tutorial.md](tutorial.md) | Guided tour |
|
||||
| [e2e-feeds-process-export.md](e2e-feeds-process-export.md) | Catalog pipeline E2E |
|
||||
| [e2e-marketing.md](e2e-marketing.md) | Marketing AI E2E |
|
||||
| [e2e-processing.md](e2e-processing.md) | Background tasks |
|
||||
| [free-tier-verify.md](free-tier-verify.md) | Free vs Enterprise proof |
|
||||
| [api-surface-smoke.md](api-surface-smoke.md) | Admin / docs / v1 |
|
||||
| [demo-user.md](demo-user.md) | Credentials |
|
||||
| [security-notes.md](security-notes.md) | AuthZ / webhooks / SSRF |
|
||||
| [status-and-gaps.md](status-and-gaps.md) | Broader product gaps |
|
||||
| [ai-full-smoke.md](ai-full-smoke.md) | Green Chat AI matrix |
|
||||
Reference in New Issue
Block a user