9241864604
2 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
9404632830 |
feat(marketplace): public /redeem?code=... voucher landing flow
#116 adds the public landing page that the franchise model relies on to convert voucher distribution into Catalyst signups (per docs/FRANCHISE-MODEL.md §3, "redemption flow end-to-end"). New page core/marketplace/src/pages/redeem.astro: - Reads ?code=... from the URL (or accepts manual entry if absent). - POSTs to /api/billing/vouchers/redeem-preview (added in #117) — does NOT consume the voucher, just validates it. - Renders one of four states: * Valid (200): "X OMR credit" + description + "Sign up to redeem" CTA. The CTA stashes the code in localStorage under `sme-pending-voucher` and routes to /plans (the start of the existing signup wizard). * Campaign ended (410): inactive or capped — shows the credit that was offered + a path to sign up without a voucher. * Not valid (404): never existed or soft-deleted (#91 tombstone-leak protection — the two are indistinguishable on the public surface). * No code present: a manual input form so a redeemer who landed on /redeem without a query string can paste their code. CheckoutStep wiring (core/marketplace/src/components/CheckoutStep.svelte): - The `promoCode` $state now hydrates from `sme-pending-voucher` so a redeemer arriving via /redeem reaches /checkout with the field pre-filled. They can still edit or clear it. - After submitting to /billing/checkout, we clear the localStorage stash. This prevents a second signup on the same browser from silently carrying over the previous voucher. The actual redemption (insert into promo_redemptions, increment times_redeemed, credit_ledger entry) still happens transactionally inside POST /billing/checkout — splitting it out would risk a partially-redeemed code with no Order to show for it (the same class of bug #91 fixed). Per docs/INVIOLABLE-PRINCIPLES.md §1: target-state shape, not MVP. The page handles all four observable backend states; manual-entry fallback is included; the "campaign ended" path keeps the user moving into signup rather than dead-ending. Closes #116. |
||
|
|
3c2f7e4cda |
feat(consolidation): Phase 1 — move Catalyst-Zero apps + CI + manifests into public monorepo
Per docs/PROVISIONING-PLAN.md Phase 1. Catalyst-Zero (the running deployment on Contabo k3s, namespaces catalyst/sme/marketplace/website) source code now lives in this public repo. Cutover to public-repo CI builds happens in Phase 2.
What moved (from openova-private → openova):
- apps/console/ → core/console/ (Astro+Svelte UI)
- apps/admin/ → core/admin/ (Astro+Svelte UI, includes canonical voucher/billing/tenants admin surface)
- apps/marketplace/ → core/marketplace/ (Astro+Svelte UI, 5-step Plan→Apps→Addons→Checkout→Review flow)
- website/marketplace-api/ → core/marketplace-api/ (Go backend with handlers/, provisioner/, store/)
- clusters/contabo-mkt/apps/catalyst/ → products/catalyst/chart/templates/ (catalyst-{ui,api} K8s manifests)
- clusters/contabo-mkt/apps/sme/services/ → products/catalyst/chart/templates/sme-services/ (15 manifests)
- clusters/contabo-mkt/apps/marketplace-api/ → products/catalyst/chart/templates/marketplace-api/
- 5 CI workflows (catalyst-build, marketplace-api-build, sme-{admin,console,marketplace}-build) → .github/workflows/, renamed to drop "sme-" prefix
Image refs updated:
- ghcr.io/openova-io/openova-private/catalyst-{ui,api} → ghcr.io/openova-io/openova/catalyst-{ui,api}
- ghcr.io/openova-io/openova-private/sme-{admin,console,marketplace} → ghcr.io/openova-io/openova/{admin,console,marketplace}
- ghcr.io/openova-io/openova-private/marketplace-api → ghcr.io/openova-io/openova/marketplace-api
Workflow path updates:
- paths: 'apps/{X}/**' → 'core/{X}/**'
- context: apps/{X} → core/{X}
- deploy paths: clusters/contabo-mkt/apps/{X}/.../{X}.yaml → products/catalyst/chart/templates/.../{X}.yaml
- deploy commit: git add clusters/ → git add products/
Deferred to follow-up phase:
- 8 legacy SME backend services (auth, billing, catalog, domain, gateway, notification, provisioning, tenant) keep their ghcr.io/openova-io/openova-private/sme-* image refs because their source code in openova-private/services/ has not yet been migrated to public repo. Tracked via TODO in core/README.md migration history.
- sme-services-build.yaml NOT migrated (matches deferred services).
Documentation updates:
- core/README.md rewritten to describe what's actually in this directory now (4 deployed modules, not the old Go-monorepo placeholder design)
- products/catalyst/README.md created with migration status table
- products/catalyst/chart/Chart.yaml created (umbrella bp-catalyst-platform chart)
- docs/IMPLEMENTATION-STATUS.md §1 + §2.1 + §6 updated: console/admin/marketplace/marketplace-api/catalyst-{ui,api} all flipped from 📐 to 🚧 (deployed but not yet wired to unified Catalyst contract); openova Sovereign description rewritten to make Catalyst-Zero status explicit; omantel target updated to omantel.omani.works on Hetzner.
Verification:
- 99 source files copied (verified via git ls-files count)
- All image refs updated except the 8 deferred legacy SME backend services (verified via grep openova-private)
- Workflow naming reflects unified Catalyst (no more "sme-" prefix)
Phase 2 next: trigger public-repo CI builds, GHCR images published under openova/ namespace, Flux source on Catalyst-Zero repointed to this repo, rolling update of Contabo pods to new image SHAs. Catalyst-Zero becomes self-built from the public repo.
|