Commit Graph

4 Commits

Author SHA1 Message Date
e3mrah
a11067da1a
fix(gateway): /redeem-preview + plans + addons must be public (D29) (#1559)
* feat(billing+notification): wire voucher-issued email (D28)

D28 of the Sovereign DoD requires that issuing a voucher emails it to
the recipient zero-touch. Today POST /billing/vouchers/issue persists
the PromoCode row but never notifies anyone — so a gifted voucher only
reaches its recipient if the operator manually sends the code over a
side channel. This wires sme-billing -> sme-notification so the email
fires automatically on every successful upsert that carries a
recipient_email field.

Architecture follows the existing notification-service seam:
sme-billing POSTs to http://notification.sme.svc.cluster.local:8087/
notification/send with template=voucher-issued; sme-notification renders
the HTML and dispatches via Stalwart over SMTP. No direct SMTP code is
added to billing, no stalwart-mail calls bypass notification.

Server-side only — the owner-UI for issuing vouchers (D28b) is a
separate PR.

Changes:

  notification/templates/templates.go
    + VoucherIssuedEmail(code, creditOMR, description, sovereignFQDN,
      validityHint) — renders code prominently, redeem button to
      https://marketplace.<sovereignFQDN>/redeem/?code=<CODE>; FQDN
      always supplied by caller, NEVER hardcoded.

  notification/handlers/handlers.go
    + renderTemplate("voucher-issued") case parsing
      {code, credit_omr, description, sovereign_fqdn, validity_hint}.
    + Default subject "You've been gifted a voucher for OpenOva SME".

  billing/handlers/handlers.go
    + Handler fields: NotificationURL, SovereignFQDN, NotificationClient.

  billing/handlers/vouchers.go
    + issueVoucherRequest = store.PromoCode + RecipientEmail (request-
      only; never persisted).
    + sendVoucherIssuedEmail() — POSTs to NotificationURL with a 5s
      timeout. Best-effort: a non-2xx or transport error logs but does
      NOT fail the IssueVoucher response, because the row is already
      persisted and re-issuing the same code re-fires the email.
    + Re-issue semantics (#91 resurrects soft-deleted rows) extend to
      the email path — documented in the handler comment.

  billing/main.go
    + Reads NOTIFICATION_SERVICE_URL (default
      http://notification.sme.svc.cluster.local:8087/notification/send)
      and SOVEREIGN_FQDN env vars. Wires a 5s default http.Client.

  products/catalyst/chart/templates/sme-services/billing.yaml
    + Pipes NOTIFICATION_SERVICE_URL (cluster-DNS constant) and
      SOVEREIGN_FQDN (from .Values.global.sovereignFQDN, NEVER
      hardcoded) into the billing Deployment.

Tests:

  notification/handlers/handlers_test.go (new)
    + TestRenderTemplate_VoucherIssued: rendered HTML contains code +
      credit + a redeem URL built from the supplied FQDN; never falls
      back to marketplace.openova.io.
    + TestRenderTemplate_VoucherIssued_CustomSubject + _NoDescription
      + TestRenderTemplate_UnknownTemplate as guard rails.

  billing/handlers/vouchers_test.go
    + TestIssueVoucher_SendsEmail_WhenRecipientPresent: a fake round-
      tripper sees the POST to notification with the right URL +
      template + data (code upper-cased, credit_omr, sovereign_fqdn,
      description) when recipient_email is set.
    + TestIssueVoucher_NoEmail_WhenRecipientAbsent: no notification
      call when recipient is empty.
    + TestIssueVoucher_NotificationFailure_DoesNotFailUpsert:
      operator gets 200 even when notification returns 500.
    + TestIssueVoucher_403WithoutVoucherIssuerRole: role gate preserved.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(chart): admin pod uses dedicated image tag (D27 SME stack)

t132 caught admin pod stuck in ImagePullBackOff on `admin:b0ed216` —
the SME services CI run for that mono-repo SHA published 10 services
but admin's image was missing from GHCR. Decouple admin's tag from
smeTag so a missing-build for one service doesn't wedge the SME stack.

Default to `3c2f7e4` (matches marketplaceApi + console, known-published).
When admin's UI changes, bump in lockstep with those.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* chore(slot-13): pin bp-catalyst-platform to 1.4.144

PR #1556 (D28 voucher email wire) + PR #1557 (D27 admin tag override)
landed and Blueprint Release packaged 1.4.144. Pin the slot file so
future provisions get the latest chart by default — t132 manually
upgraded via kubectl patch but t133+ will inherit it.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(gateway): /redeem-preview + plans + addons must be public (D29)

The marketplace /redeem?code=XXX landing page calls
/api/billing/vouchers/redeem-preview unauthenticated per docs/FRANCHISE-
MODEL.md §3, but the gateway's catch-all /api/billing/ entry was
returning 401 to it — breaking the entire voucher-redeem zero-touch
flow that D29 depends on.

Also expose /api/billing/plans and /api/billing/addons so the
marketplace landing can render pricing without a session.

Caught live on t132 2026-05-16 — every /redeem call returned 401.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: hatiyildiz <hatice.yildiz@openova.io>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 23:17:04 +04:00
e3mrah
fa4395fa3a
fix(bp-catalyst-platform): wire VALKEY_PASSWORD into SME auth + gateway (#863) (#864)
After PR #862 (1.4.4) made cross-ns Valkey reachable from `sme` ns, the
auth Pod started CrashLoopBackOff with "NOAUTH HELLO must be called with
the client already authenticated". Root cause: bp-valkey 1.0.0 ships
auth.enabled=true (bitnami default) but SME service code + Deployment
templates never plumbed a password through.

Chart 1.4.4 -> 1.4.5. Slot 13 pin lockstep.

Changes:
- core/services/shared/db/valkey.go: add ConnectValkeyWithAuth overload
  taking username + password. ConnectValkey kept backwards-compatible
  for contabo-mkt's auth-less in-namespace Valkey.
- core/services/auth/main.go + gateway/main.go: read VALKEY_USERNAME +
  VALKEY_PASSWORD env, call ConnectValkeyWithAuth when password set,
  else fall through to no-auth path.
- NEW templates/sme-services/valkey-cross-ns-secret.yaml: Helm `lookup`
  reads bp-valkey's auto-generated `valkey-password` from the
  `valkey/valkey` Secret and re-emits it as `sme-valkey-auth` in `sme`
  ns. Same pattern as sme-secrets.yaml (#859) and gitea-admin-secret
  (#830 Bug 2). On first install the lookup may return nil; Flux's 15m
  reconcile picks up the mirror once bp-valkey is Ready.
- auth.yaml + gateway.yaml: add VALKEY_PASSWORD env from `sme-valkey-
  auth` Secret with optional=true so contabo-mkt's auth-less path keeps
  working when the mirror Secret is absent.
- values.yaml: add `smeServices.valkey.{sourceSecretName,
  sourcePasswordKey, destNamespace, destSecretName}` knobs (Inviolable
  Principle #4).

Live verified the failure mode on otech103: 11/13 SME pods Running 1/1,
auth in CrashLoopBackOff with NOAUTH HELLO error. Provisioning Pod's
CreateContainerConfigError is unrelated (ghcr-pull, separate ticket).

Co-authored-by: hatiyildiz <hatice.yildiz@openova.io>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 06:09:38 +04:00
e3mrah
5cdb738ac9
fix(services): go mod tidy across sibling services after #798 shared deps bump (#821)
#798 added github.com/nats-io/nats.go to core/services/shared/go.mod and
adjusted x/sys/x/crypto/x/text to Go 1.22-compatible versions. The
sibling services (auth, catalog, domain, gateway, notification,
provisioning, tenant) reference the same shared module via the local
`replace` directive — their go.sum files must include the new transitive
hashes, otherwise the CI Containerfile build hits:

    go: updates to go.mod needed; to update it: go mod tidy

This commit is a pure `go mod tidy` across all 7 services; no source
changes. CI services-build is now unblocked.

Co-authored-by: hatiyildiz <hatice.yildiz@openova.io>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-04 22:35:46 +04:00
hatiyildiz
7646840ffe feat(consolidation): move 8 SME backend services + shared module to public repo
Per docs/PROVISIONING-PLAN.md and tickets [B] sme-backend group. Migrates the 8 Go backend services from openova-private/services/ to openova/core/services/, plus the shared module they all depend on, plus the services-build CI workflow.

What moved:
- services/auth → core/services/auth (Go HTTP service for SME marketplace authentication)
- services/billing → core/services/billing (Go HTTP service for billing + voucher backend)
- services/catalog → core/services/catalog (Go HTTP service for App catalog)
- services/domain → core/services/domain (Go HTTP service for tenant domain mapping)
- services/gateway → core/services/gateway (Go HTTP gateway with rate limiting)
- services/notification → core/services/notification (Go HTTP service with email templates)
- services/provisioning → core/services/provisioning (Go HTTP service that commits tenant Application manifests via Gitea/GitHub API)
- services/tenant → core/services/tenant (Go HTTP service for tenant lifecycle)
- services/shared → core/services/shared (shared Go module: db, events, health, middleware, respond)
- 9 go.mod files updated: module github.com/openova-io/openova-private/services/<X> → github.com/openova-io/openova/core/services/<X>
- 9 go.sum and import paths similarly updated
- replace directives updated: openova-private/services/shared → openova/core/services/shared
- sme-services-build.yaml workflow → services-build.yaml in .github/workflows/, paths/context/image-base/deploy paths all repointed at core/services + ghcr.io/openova-io/openova/services-* + products/catalyst/chart/templates/sme-services
- All 8 manifests in products/catalyst/chart/templates/sme-services/ updated: image refs ghcr.io/openova-io/openova-private/sme-{X} → ghcr.io/openova-io/openova/services-{X}
- provisioning.yaml GITHUB_REPO env var: "openova-private" → "openova"

Closes [B] sme-backend (10 tickets).

After this commit, all 14 user-facing + backend Catalyst-Zero modules build from this public repo:
- 4 UIs: console, admin, marketplace, catalyst-ui
- 2 backends: marketplace-api, catalyst-api
- 8 SME services: auth, billing, catalog, domain, gateway, notification, provisioning, tenant
- 1 shared Go module

Note: 1 line in core/services/provisioning/main.go retains a literal default of "openova-private" for the GITHUB_REPO fallback when env var is unset; the K8s manifest sets GITHUB_REPO=openova explicitly so this path is never exercised in the deployed runtime, and the in-code default will be cleaned up in a follow-up.
2026-04-28 12:30:32 +02:00