mirror of
https://github.com/hanzoai/commerce.git
synced 2026-10-30 15:08:42 +00:00
IsSuperAdmin folded the case of the home org and fell back to the owner claim, the org of the application that minted the token, and the money bits were granted on X-User-Owner == admin. Cloud writes X-User-Owner for an admin-org application and an admin-org person's API key as well as for a person, so either could mint money and edit the catalog here while being nobody in the rest of the same process. Every gate now reads X-User-IsAdmin, which the edge strips and writes from authz.Claims.Sudo alone; the standalone EdgeAuth decides it with that predicate over the verified token's signed membership set. X-User-IsAdmin no longer doubles as the org-admin bit, which is X-User-IsOrgAdmin. No machine is SuperAdmin. Global admin is SuperAdmin wherever it named this predicate.
258 lines
11 KiB
Go
258 lines
11 KiB
Go
package api
|
|
|
|
import (
|
|
"net/http"
|
|
|
|
"github.com/zap-proto/zip"
|
|
|
|
"github.com/hanzoai/commerce/api/resources"
|
|
"github.com/hanzoai/commerce/billing/paywall"
|
|
"github.com/hanzoai/commerce/config"
|
|
"github.com/hanzoai/commerce/middleware"
|
|
"github.com/hanzoai/commerce/models/payment"
|
|
"github.com/hanzoai/commerce/models/site"
|
|
"github.com/hanzoai/commerce/models/token"
|
|
|
|
// "github.com/hanzoai/commerce/models/transaction"
|
|
|
|
"github.com/hanzoai/commerce/models/user"
|
|
"github.com/hanzoai/commerce/util/permission"
|
|
"github.com/hanzoai/commerce/util/rest"
|
|
"github.com/hanzoai/commerce/util/router"
|
|
|
|
accessTokenApi "github.com/hanzoai/commerce/api/accesstoken"
|
|
accountApi "github.com/hanzoai/commerce/api/account"
|
|
affiliateApi "github.com/hanzoai/commerce/api/affiliate"
|
|
apikeyApi "github.com/hanzoai/commerce/api/apikey"
|
|
authApi "github.com/hanzoai/commerce/api/auth"
|
|
b2bApi "github.com/hanzoai/commerce/api/b2b"
|
|
billingApi "github.com/hanzoai/commerce/api/billing"
|
|
cartApi "github.com/hanzoai/commerce/api/cart"
|
|
catalogApi "github.com/hanzoai/commerce/api/catalog"
|
|
cdnApi "github.com/hanzoai/commerce/api/cdn"
|
|
checkoutApi "github.com/hanzoai/commerce/api/checkout"
|
|
claimApi "github.com/hanzoai/commerce/api/claim"
|
|
costsApi "github.com/hanzoai/commerce/api/costs"
|
|
counterApi "github.com/hanzoai/commerce/api/counter"
|
|
couponApi "github.com/hanzoai/commerce/api/coupon"
|
|
currencyApi "github.com/hanzoai/commerce/api/currency"
|
|
customergroupApi "github.com/hanzoai/commerce/api/customergroup"
|
|
dataApi "github.com/hanzoai/commerce/api/data"
|
|
deployApi "github.com/hanzoai/commerce/api/deploy"
|
|
draftorderApi "github.com/hanzoai/commerce/api/draftorder"
|
|
exchangeApi "github.com/hanzoai/commerce/api/exchange"
|
|
formApi "github.com/hanzoai/commerce/api/form"
|
|
fulfillmentApi "github.com/hanzoai/commerce/api/fulfillment"
|
|
giftcardApi "github.com/hanzoai/commerce/api/giftcard"
|
|
inventoryApi "github.com/hanzoai/commerce/api/inventory"
|
|
inviteApi "github.com/hanzoai/commerce/api/invite"
|
|
libraryApi "github.com/hanzoai/commerce/api/library"
|
|
metricsApi "github.com/hanzoai/commerce/api/metrics"
|
|
namespaceApi "github.com/hanzoai/commerce/api/namespace"
|
|
notificationApi "github.com/hanzoai/commerce/api/notification"
|
|
orderApi "github.com/hanzoai/commerce/api/order"
|
|
organizationApi "github.com/hanzoai/commerce/api/organization"
|
|
payablesApi "github.com/hanzoai/commerce/api/payables"
|
|
planApi "github.com/hanzoai/commerce/api/plan"
|
|
pricingApi "github.com/hanzoai/commerce/api/pricing"
|
|
producttaxonomyApi "github.com/hanzoai/commerce/api/producttaxonomy"
|
|
promoApi "github.com/hanzoai/commerce/api/promo"
|
|
promotionApi "github.com/hanzoai/commerce/api/promotion"
|
|
referralApi "github.com/hanzoai/commerce/api/referral"
|
|
regionApi "github.com/hanzoai/commerce/api/region"
|
|
reviewApi "github.com/hanzoai/commerce/api/review"
|
|
searchApi "github.com/hanzoai/commerce/api/search"
|
|
storeApi "github.com/hanzoai/commerce/api/store"
|
|
subscriptionApi "github.com/hanzoai/commerce/api/subscription"
|
|
taxApi "github.com/hanzoai/commerce/api/tax"
|
|
transactionApi "github.com/hanzoai/commerce/api/transaction"
|
|
uploadApi "github.com/hanzoai/commerce/api/upload"
|
|
userApi "github.com/hanzoai/commerce/api/user"
|
|
xdApi "github.com/hanzoai/commerce/api/xd"
|
|
|
|
dashv2Api "github.com/hanzoai/commerce/api/dashv2"
|
|
bitcoinApi "github.com/hanzoai/commerce/thirdparty/bitcoin/api"
|
|
mercuryApi "github.com/hanzoai/commerce/thirdparty/mercury/api"
|
|
paypalApi "github.com/hanzoai/commerce/thirdparty/paypal/ipn"
|
|
reamazeApi "github.com/hanzoai/commerce/thirdparty/reamaze"
|
|
shipstationApi "github.com/hanzoai/commerce/thirdparty/shipstation"
|
|
shipwireApi "github.com/hanzoai/commerce/thirdparty/shipwire/api"
|
|
|
|
// Side effect import because of cyclical dependency
|
|
_ "github.com/hanzoai/commerce/models/referrer/tasks"
|
|
)
|
|
|
|
func Route(api *zip.Group) {
|
|
tokenRequired := middleware.TokenRequired()
|
|
adminRequired := middleware.TokenRequired(permission.Admin)
|
|
|
|
// Subscription paywall — the org-level admin gate ($20/mo pro plan OR live
|
|
// trial OR redeemed invite). Mounted as a sibling to tokenRequired, AFTER it
|
|
// so the org is resolved, on the merchant admin surface. It supersedes the
|
|
// store-bound requireAccess (a strict superset: same subscription
|
|
// check across both money stores, plus trial-credit and invite paths).
|
|
// billing/subscribe/trial/invite/catalog routes are NOT behind it — those are
|
|
// how an org acquires access, or are public reads.
|
|
requireAccess := paywall.Require
|
|
|
|
// Index
|
|
if config.IsDevelopment {
|
|
api.Raw(http.MethodGet, "/", middleware.ParseToken, rest.ListRoutes())
|
|
} else {
|
|
api.Raw(http.MethodGet, "/", router.Ok)
|
|
api.Raw(http.MethodHead, "/", router.Empty)
|
|
}
|
|
|
|
// Use permissive CORS policy for all API routes.
|
|
api.Use(middleware.AccessControl("*"))
|
|
api.Raw(http.MethodOptions, "*wildcard", func(c *zip.Ctx) error {
|
|
return c.Next()
|
|
})
|
|
|
|
// NOTE: there is no HTTP dispatch route for delayed funcs. A delayed func is
|
|
// a typed Go value invoked in process (util/task: `v.Call(ctx, args...)`,
|
|
// with delay.Function verifying AssignableTo at enqueue). The old
|
|
// POST /_/queue/delay route decoded a gob body, looked the function up BY
|
|
// NAME and reflect-called it with caller-supplied arguments — unauthenticated,
|
|
// untyped, and a second way to do what the typed path already does.
|
|
|
|
// Checkout APIs (charge, authorize, capture)
|
|
checkoutApi.Route(api)
|
|
|
|
subscriptionApi.Route(api)
|
|
|
|
// The merchant resources. ONE table, in its own leaf package so the cloud
|
|
// plugin can mount the same set at /v1/commerce/<kind> without importing
|
|
// this package's checkout, subscription and thirdparty tree.
|
|
resources.Route(api, tokenRequired, adminRequired, requireAccess, publishProductEvents)
|
|
|
|
// Site stays HERE, not in the leaf. models/site carries a Netlify deploy
|
|
// client, so a leaf importing it would drag netlify-go into every consumer —
|
|
// the exact weight that kept the merchant table out of the cloud embed. A Site
|
|
// is a standalone publishing surface rather than merchant CRUD, and this
|
|
// package already carries that dependency.
|
|
rest.New(site.Site{}).Route(api, tokenRequired)
|
|
|
|
paymentApi := rest.New(payment.Payment{})
|
|
paymentApi.Route(api, tokenRequired)
|
|
|
|
accountApi.Route(api, tokenRequired)
|
|
billingApi.Route(api, tokenRequired)
|
|
costsApi.Route(api, tokenRequired)
|
|
metricsApi.Route(api, tokenRequired) // SaaS ops god-view (revenue/subs/usage/customers) — SuperAdmin gated inside
|
|
|
|
cartApi.Route(api, tokenRequired)
|
|
couponApi.Route(api, tokenRequired)
|
|
// Subscription paywall invite surface: POST /v1/commerce/invite/redeem (org
|
|
// claims a code) + POST /v1/commerce/invite (platform-admin mint). NOT behind
|
|
// requireAccess — redeeming a code is how an org acquires access.
|
|
inviteApi.Route(api, tokenRequired)
|
|
deployApi.Route(api, tokenRequired)
|
|
formApi.Route(api, tokenRequired)
|
|
inventoryApi.Route(api, tokenRequired, requireAccess)
|
|
orderApi.Route(api, tokenRequired, requireAccess)
|
|
referralApi.Route(api, tokenRequired)
|
|
payablesApi.Route(api, adminRequired) // what we owe + manual payment records (RequirePlatformAdmin inside)
|
|
affiliateApi.Route(api, tokenRequired)
|
|
regionApi.Route(api, tokenRequired)
|
|
reviewApi.Route(api, tokenRequired)
|
|
storeApi.Route(api, tokenRequired)
|
|
transactionApi.Route(api, tokenRequired)
|
|
uploadApi.Route(api, adminRequired) // admin product-image uploads → S3 (tenant-prefixed, image-only)
|
|
userApi.Route(api, tokenRequired)
|
|
pricingApi.Route(api, tokenRequired, requireAccess)
|
|
promotionApi.Route(api, tokenRequired, requireAccess)
|
|
promoApi.Route(api) // SuperAdmin plan-promo config (/v1/platform/promo) — RequirePlatformAdmin inside
|
|
|
|
// Medusa-parity admin domains. These sub-routers were fully implemented
|
|
// (models orm.Register'd, tenant-scoped via middleware.Namespace) but never
|
|
// wired into the /v1 bundle, so their routes 404'd in production. Wiring
|
|
// them here completes the fulfillment/shipping, tax, customer-group,
|
|
// publishable-api-key/RBAC, and notification admin domains.
|
|
fulfillmentApi.Route(api, tokenRequired, requireAccess) // fulfillment sets/providers, shipping options/profiles, service+geo zones, ship/cancel
|
|
taxApi.Route(api, tokenRequired, requireAccess) // tax regions/rates/rules/providers + /tax/calculate
|
|
customergroupApi.Route(api, tokenRequired)
|
|
apikeyApi.Route(api, tokenRequired) // publishable API keys, roles, api permissions
|
|
notificationApi.Route(api, tokenRequired)
|
|
giftcardApi.Route(api, adminRequired, requireAccess) // gift cards + idempotent redeem/void (money — admin only)
|
|
b2bApi.Route(api, tokenRequired, requireAccess) // B2B: companies, employees, quotes, approvals
|
|
exchangeApi.Route(api, tokenRequired) // order exchanges (return + replacement)
|
|
currencyApi.Route(api, adminRequired) // currency reference table CRUD (global default-ns; public list on commerce group)
|
|
claimApi.Route(api, tokenRequired) // order claims (damaged/wrong/missing → refund or replacement)
|
|
draftorderApi.Route(api, tokenRequired, requireAccess) // admin order builder: draft orders + line items → complete into a real order
|
|
producttaxonomyApi.Route(api, tokenRequired) // product options/values, categories, tags, types, return/refund reasons
|
|
catalogApi.AdminRoute(api, adminRequired) // platform product catalog CMS (SuperAdmin gated inside)
|
|
// Public catalog projection GET /v1/commerce/catalog is wired on the
|
|
// commerce public group (commerce.go) so it serves that exact path.
|
|
|
|
// Platform subscription/DNS plan authority CRUD (SuperAdmin gated inside).
|
|
// Public read stays GET /v1/billing/plans; the embed seed source is injected
|
|
// here (the composition root) so api/plan stays free of an api/billing import.
|
|
planApi.AdminRoute(api, billingApi.SeedRows, adminRequired)
|
|
|
|
// Hanzo APIs, using default namespace (internal use only)
|
|
organizationApi.Route(api, tokenRequired)
|
|
|
|
token := rest.New(token.Token{})
|
|
token.DefaultNamespace = true
|
|
token.Prefix = "/c/"
|
|
token.Route(api, tokenRequired)
|
|
|
|
user := rest.New(user.User{})
|
|
user.DefaultNamespace = true
|
|
user.Prefix = "/c/"
|
|
user.Route(api, tokenRequired)
|
|
|
|
searchApi.Route(api, tokenRequired)
|
|
|
|
// Namespace API
|
|
namespaceApi.Route(api)
|
|
|
|
// Access token API
|
|
accessTokenApi.Route(api)
|
|
|
|
// OAuth API
|
|
authApi.Route(api)
|
|
|
|
// Reamaze custom store API endpoints
|
|
reamazeApi.Route(api)
|
|
|
|
// Shipstation custom store API endpoints
|
|
shipstationApi.Route(api)
|
|
|
|
// Shipwire custom store API endpoints
|
|
shipwireApi.Route(api)
|
|
|
|
// Paypal IPN
|
|
paypalApi.Route(api)
|
|
|
|
// Data Api
|
|
dataApi.Route(api)
|
|
|
|
// XDomain proxy.html
|
|
xdApi.Route(api)
|
|
|
|
// Routes from deprecated cdn module
|
|
cdnApi.Route(api)
|
|
|
|
// dashv2
|
|
dashv2Api.Route(api)
|
|
|
|
// Counter Api (admin only)
|
|
counterApi.Route(api)
|
|
|
|
// Library Api
|
|
libraryApi.Route(api)
|
|
|
|
// Marketing routes moved to github.com/hanzoai/marketing
|
|
|
|
// Bitcoin webhook
|
|
bitcoinApi.Route(api)
|
|
|
|
// Mercury bank webhook
|
|
mercuryApi.Route(api)
|
|
|
|
// Extra routes registered by optional sub-modules (e.g. luxfi/cevm)
|
|
applyExtraRoutes(api)
|
|
}
|