11 Commits
Author SHA1 Message Date
5d9dbc5006 debrand(temporal): scrub @temporalio / nestjs-temporal-core from Hanzo Tasks integration comments (#7)
Imports were migrated to @hanzoai/tasks in social#2; these were the last
documentation-comment references to the retired Temporal brand tokens.
Reworded to the equivalent generic phrasing (legacy client shape,
durable-execution NestJS module, proxyActivities API) — meaning unchanged,
comment-only, compile-neutral. social now has zero go.temporal.io /
@temporalio / nestjs-temporal-core references.

Co-authored-by: Hanzo Dev <dev@hanzo.ai>
2026-07-05 11:09:34 -07:00
Hanzo DevandGitHub b2a6d092cf feat(orchestrator): migrate off Temporal to @hanzoai/tasks (embedded engine, gated ZAP) (#2)
Replace @temporalio/* + nestjs-temporal-core with @hanzoai/tasks. Durable
workflow execution now runs on the ONE Hanzo Tasks engine embedded in cloud,
reached over its identity-gated ZAP listener (cloud.hanzo.svc:9999) and
authenticated with a Hanzo IAM client_credentials bearer (owner-scoped). No
upstream Temporal anywhere.

- libraries/.../temporal/tasks.ts: @hanzoai/tasks-backed NestJS integration —
  @Activity/@ActivityMethod decorators, TemporalService facade
  (client.getRawClient()/getWorkflowHandle(), terminateWorkflow()), IAM
  client_credentials token source, getTemporalModule(). Connects a Client +
  one Worker per task queue (main + per social integration), RegisterNamespace
  on init.
- temporal.module.ts re-exports getTemporalModule; temporal.register.ts is a
  no-op (search attrs need no pre-registration on Hanzo Tasks);
  infinite.workflow.register.ts + health.controller.ts use @hanzoai/tasks;
  backend main.ts drops the Temporal worker Runtime.install.
- temporal/workflow.ts: workflow-side barrel re-exporting @hanzoai/tasks with a
  drop-in proxyActivities (unconstrained generic + cancellationType superset)
  so proxyActivities<ActivityClass>() type-checks as it did under @temporalio.
- All 11 workflows + 4 activity classes + 2 signals ported; uses proxyActivities,
  sleep, condition, defineSignal/setHandler (signal-to-running-wf), continueAsNew
  (digest/sendEmail), startChild (post repeat), typed search attributes,
  workflowIdConflictPolicy.
- @hanzoai/tasks vendored as a tarball (file: dep); TEMPORAL_ADDRESS scrubbed,
  new TASKS_ADDRESS default cloud.hanzo.svc:9999 / TASKS_NAMESPACE default.

Orchestrator + backend build green (nest build, full typecheck).
2026-07-04 22:12:42 -07:00
zeekay 002b70e0e7 prisma: pin debian-openssl-3.0.x engine + install openssl in images
slim images lack the openssl CLI; Prisma's platform detection guessed
openssl-1.1.x at runtime and the orchestrator boot-looped on a missing
query engine.
2026-06-10 23:09:14 -07:00
zeekay fa59876b91 billing: align to commerce v1 API; storage: S3-endpoint override; deps: @hanzo/{plans,wallets}
- billing.service: COMMERCE_TOKEN service-token auth (IAM M2M fallback),
  cancel via POST …/cancel {atPeriodEnd}, prorate via PATCH {prorate:true},
  currentPeriod* field names, local subscription write on subscribe (no
  commerce webhook emitter yet), channel limits from @hanzo/plans
- upload: CLOUDFLARE_ENDPOINT overrides R2 endpoint (path-style) for Hanzo S3
- deps: @hanzo/plans ^1.1.3 (typed), @hanzo/wallets ^0.1.0 (rebranded)
- frontend: type-only @prisma/client imports (turbopack chokes on value
  imports of the node runtime in client chunks)
- typecheck: backend tsc clean
2026-06-10 21:39:29 -07:00
zeekay fea2a2612f rebrand: full identifier sweep — @social/ paths, social-* pkgs, zero postiz/gitroom
Complete rebrand. Code now reads as a first-party Hanzo project, not
a forked codebase with upstream identifiers leaking through.

tsconfig path aliases (9 entries):
  @gitroom/backend          → @social/backend
  @gitroom/frontend         → @social/frontend
  @gitroom/helpers          → @social/helpers
  @gitroom/nestjs-libraries → @social/nestjs-libraries
  @gitroom/react            → @social/react
  @gitroom/plugins          → @social/plugins
  @gitroom/orchestrator     → @social/orchestrator
  @gitroom/extension        → @social/extension

Workspace package names (apps/*/package.json):
  postiz-backend       → social-backend
  postiz-frontend      → social-frontend
  postiz-orchestrator  → social-orchestrator
  postiz-extension     → social-extension
  postiz-command       → social-command
  @postiz/node         → @social/node

Swept across 477 files: every import statement, env var (POSTIZ_*→SOCIAL_*),
identifier (featured_by_gitroom→featured_by_social, FEATURED_BY_GITROOM enum
key→FEATURED_BY_SOCIAL), prose mention, FAQ string, and i18n value in all
23 locale JSONs.

Preserved (required by AGPL-3.0): upstream-fork attribution in SECURITY.md,
README.md, CLAUDE.md ("hard fork of gitroomhq/postiz-app"). Required for
license compliance; not used by any code path.

Preserved (external npm dep): @postiz/wallets — this is a published wallet
provider package, not workspace code. We pull it as a normal dependency.

Verification: `grep -riE "postiz|gitroom" --include={ts,tsx,json,mjs,js,md,yaml,yml}`
excluding node_modules + legal docs + i18n.lock returns 0.
2026-06-10 21:39:29 -07:00
zeekay e4184fb989 billing: rip out Stripe entirely — Hanzo commerce + billing only
Per "must use ~/work/hanzo/commerce native ~/work/hanzo/billing no other
shit": removed every line of Stripe code from the codebase. Not feature-
flagged — there is no Stripe code path anymore.

Deletes:
- libraries/nestjs-libraries/src/services/stripe.service.ts (986 lines).
- apps/backend/src/api/routes/stripe.controller.ts.
- Stripe deps: stripe, @stripe/stripe-js, @stripe/react-stripe-js.

Renames + rewrites:
- hanzo-billing.service.ts → billing.service.ts; class
  HanzoBillingService → BillingService.
- StripeService injection token swept to BillingService across
  api.module, public.api.module, database.module, billing.controller,
  users.controller.
- _stripeService → _billingService in every consumer.

New: apps/backend/src/api/routes/commerce.controller.ts (replaces
StripeController). POST /commerce receives commerce.hanzo.ai webhooks;
HMAC-SHA256(X-Commerce-Signature, body, COMMERCE_WEBHOOK_SECRET)
verified in constant time by BillingService.validateRequest. Event
dispatch: subscription.{created,updated,deleted}, invoice.payment_succeeded.
Webhook URL: https://social.hanzo.ai/api/commerce (api prefix stripped
to /commerce by traefik social-strip-api middleware).

BillingService now fully implements every method billing.controller calls:
- subscribe()              → POST /v1/billing/subscriptions (commerce.hanzo.ai).
                             Skips local db write — webhook is authoritative.
- embedded()               → returns { portal: "billing.hanzo.ai/checkout?plan=…" }
                             (NO Stripe Elements; UI redirects to portal).
- cancelSubscription()     → POST /v1/billing/subscriptions/{id}/cancel
- setToCancel()            → PATCH …/subscriptions/{id} {cancelAtPeriodEnd:true}
- createBillingPortalLink()→ BILLING_PORTAL_URL (default billing.hanzo.ai)
- checkSubscription()      → GET /v1/billing/subscriptions/{id}
- getCharges()             → GET /v1/billing/transactions
- refundCharges()          → POST /v1/billing/payments/{id}/refund (per id)
- lifetimeDeal()           → validate discount code + grant credit
- prorate()                → PATCH subscription planId (commerce handles math)
- getPackages()            → @hanzo/plans social-* tiers
- checkDiscount/applyDiscount/finishTrial → portal-managed (no-op)
- createSubscription/updateSubscription/deleteSubscription/paymentSucceeded
  → webhook handlers; map planId→legacy tier; call SubscriptionService.

Frontend:
- embedded.billing.tsx: gutted Stripe Elements; renders a "Continue to
  checkout" button that redirects to the portal URL returned by
  /billing/embedded.
- first.billing.component.tsx: drop loadStripe + stripe state; pass
  data.portal directly.
- variable.context.tsx + layout.tsx (app/provider/extension): drop the
  stripeClient field — nothing consumed it.

Env sweep:
- STRIPE_PUBLISHABLE_KEY → BILLING_ENABLED (in 11 files: backend
  controllers, middlewares, orchestrator activities, FE layouts).
- STRIPE_SECRET_KEY → BILLING_ENABLED (auth middleware gates).
- STRIPE_SIGNING_KEY → COMMERCE_WEBHOOK_SECRET.

No backwards-compat shims. No "let's keep stripe.service.ts around just
in case." The Stripe SDK is uninstalled and unreachable.
2026-06-10 21:39:29 -07:00
zeekay 30e637bb8f auth: federate Postiz Organization from IAM owner claim
When a user signs in with Hanzo IAM for the first time, Postiz
auto-creates an Organization (per upstream auth.service.ts pattern).
We now rename that Organization to match the IAM `owner` claim (the
IAM org slug) so subsequent sign-ins from the same IAM org land in
the same Postiz Organization and the team membership grows organically.

Wiring:
- organization.repository.ts: add updateOrgName(orgId, name) — direct
  Prisma update of organization.name.
- organization.service.ts: pass-through updateOrgName.
- hanzo-iam.provider.ts: inject OrganizationService in constructor;
  implement postRegistration(providerToken, orgId) that GETs
  ${IAM_URL}/oauth/userinfo, extracts `owner` (fallback to
  `preferred_username`), and calls updateOrgName.

postRegistration errors are silently swallowed (auth.service does the
same wrap) so registration never fails on federation glitches; user
just keeps the form-supplied company name.

Known limitation: "first writer wins" — two new users from the same
IAM org racing through registration both get separate Postiz orgs
named with the same slug. Merging duplicates + moving memberships is
a follow-up hardening task (not currently tracked).
2026-06-10 21:39:29 -07:00
zeekay 06ddb1c5a1 billing: route through commerce.hanzo.ai when HANZO_BILLING_ENABLED=true
Adds HanzoBillingService — a Postiz-shaped wrapper around
commerce.hanzo.ai/v1/billing/* that mirrors StripeService's public
method shape so billing.controller.ts injects unchanged.

Core flows wired against commerce.hanzo.ai:
- subscribe()                 → POST /v1/billing/subscriptions
- cancelSubscription()        → POST /v1/billing/subscriptions/{id}/cancel
- setToCancel()               → PATCH /v1/billing/subscriptions/{id} {cancelAtPeriodEnd:true}
- checkSubscription()         → GET  /v1/billing/subscriptions/{id}
- createBillingPortalLink()   → https://billing.hanzo.ai (IAM-session portal)
- getCharges()                → GET /v1/billing/transactions
- getPackages()               → @hanzo/plans social-* tiers (no API hop)
- getCustomerByOrganizationId → IAM sub from org.paymentId

Legacy Postiz tier names map to Hanzo plan slugs (same table as pricing.ts):
  FREE → social-free, STANDARD → social-pro, TEAM → social-team,
  PRO → social-team-max, ULTIMATE → social-enterprise.

Discount surface (checkDiscount/applyDiscount/finishTrial) returns
no-op — Hanzo billing manages eligibility centrally via the portal.

Deferred (throws NotImplementedException — explicit follow-up):
- embedded payment form (Square Web SDK token flow needed on FE)
- lifetimeDeal (no lifetime SKU on commerce.hanzo.ai)
- prorate (mid-cycle credit math)
- refundCharges (refund API still being designed)

Webhook validateRequest/createSubscription/updateSubscription return
no-ops — commerce.hanzo.ai uses its own HMAC-signed webhook flow, not
Stripe's. Wiring the new webhook handler is tracked separately.

Module wiring: api.module.ts feature-flags { provide: StripeService,
useClass: HANZO_BILLING_ENABLED ? HanzoBillingService : StripeService }
so existing controllers keep injecting `StripeService` by token; the
actual class is swapped at boot. Per-class type laundered through
`as unknown as typeof StripeService` because the two classes share a
duck-typed interface but not a declared one — fixing properly = extract
an IBillingService interface (tracked).
2026-06-10 21:39:29 -07:00
zeekay 4beca32744 pricing: source from @hanzo/plans instead of hardcoded tiers
Replaces the upstream Postiz hardcoded pricing tiers
(FREE/STANDARD/TEAM/PRO/ULTIMATE) with a runtime adapter over the
canonical @hanzo/plans social-* tiers.

Mapping:
- FREE      → social-free        ($0)
- STANDARD  → social-pro         ($29)
- TEAM      → social-team        ($39)
- PRO       → social-team-max    ($49)
- ULTIMATE  → social-enterprise  ($99)

Why @hanzo/plans (synchronous package import) rather than a runtime
fetch to pricing.hanzo.ai: the existing consumers (subscription.service,
permissions.service, integrations.controller, users.controller,
public.controller, impersonate.tsx) access pricing.FREE/PRO/etc.
synchronously at module scope. An async fetch would force every
consumer to await or initialize-once-then-throw-if-stale. The package
import gives us the same source of truth (pricing.hanzo.ai reads the
same JSON from @hanzo/plans) without a network dependency.

Adds @hanzo/plans@^1.1.2 to root package.json — pnpm install will
resolve it from the public npm registry on next build.

PricingInnerInterface shape preserved verbatim; downstream code
unchanged.
2026-06-10 21:39:29 -07:00
zeekay cb68726e4e rebrand: sweep user-facing Postiz strings + swap logos
Brand sweep (71 .ts/.tsx/.json/.mjs files):
- "Postiz" → "Hanzo Social" (case-sensitive — preserves lowercase
  identifiers like @gitroom/postiz-* package names, env vars POSTIZ_*,
  i18n keys faq_postiz_*).
- "postiz.com" → "social.hanzo.ai"
- "https://postiz.com" → "https://social.hanzo.ai"

Covered surfaces:
- Locale JSON values (en + 22 other languages) — all keys preserved.
- Frontend layouts, OAuth/auth pages, settings, billing, agents UIs.
- Email templates (libraries/nestjs-libraries/.../email-templates).
- Provider strings (tiktok/threads/telegram/mastodon/listmonk descriptions).
- Sentry initialize.sentry.ts release/dist names.
- Swagger load.swagger.ts API title.
- Browser extension manifest (name, description).
- Test data (testomonials.tsx — review copy).
- .devcontainer/devcontainer.json container name.

Skipped (legal docs, kept as upstream):
- CCLA.md, ICLA.md, CODE_OF_CONDUCT.md, CONTRIBUTING.md.

Assets (apps/frontend/public/):
- favicon.png, favicon.svg, logo.svg, logo-text.svg, postiz.svg,
  postiz-text.svg, postiz-fav.png — all replaced with canonical
  Hanzo H mark from ~/work/hanzo/cdn/hanzo/brand/. Filenames kept
  for upstream code paths that hard-code "/postiz.svg" etc.
- SECURITY.md: rewritten to point at security@hanzo.ai +
  github.com/hanzoai/social/security/advisories/new.
2026-06-10 21:39:29 -07:00
zeekay 9268974be6 import: gitroomhq/postiz-app snapshot (AGPL-3.0)
Squashed import of upstream postiz-app at gitroomhq/postiz-app@826d07d.
Full upstream history: https://github.com/gitroomhq/postiz-app
2026-06-10 21:39:28 -07:00