AUTOXSPARE Docs
Changelog

Technical changelog

Same changes, with file paths, environment variables and the traps found along the way.

The engineering view of What's new. Newest first.

September 2026

Admin login rebranded

apps/backend/src/admin/widgets/login-branding.tsx

Medusa 2.19 has no supported way to replace the login route — it is compiled into @medusajs/dashboard and the copy comes from that package's i18n bundle, not from config. What it does expose is a login.before injection zone, which renders inside the login column between the heading and the form.

So the widget supplies AUTOXSPARE branding and hides Medusa's, rather than replacing a page. It finds Medusa's markup by walking up from its own ref to the login shell and hiding every sibling of the form wrapper, instead of matching Tailwind class names that change between releases.

The screen is pinned to light. That is not taste: the wordmark is black on transparent and vanishes on the dark theme, and the admin follows the OS by default. Colours are scoped to the login shell rather than written to medusa_admin_theme, so the rest of the admin keeps the user's own choice.

Admin served same-origin

MEDUSA_BACKEND_URL=https://api.autoxspare.com

The admin bundle had backend-production-86af.up.railway.app baked in while being served from api.autoxspare.com. CORS happened to permit it, so nothing was visibly broken, but the value is inlined at build time — a redeploy is required for any change to take effect.

Curlec live

apps/backend/src/modules/curlec/

  • Amounts are sent in sen (× 100). Curlec settles MYR only and the provider refuses any other currency outright.
  • Signature is HMAC-SHA256 over order_id|payment_id.
  • FPX requires callback_url with redirect: true; the return lands on apps/storefront/src/app/[countryCode]/(checkout)/checkout/curlec-return/route.ts, which accepts both POST and GET and answers with a 303.
  • Mode is chosen by CURLEC_MODE (test | live), which selects between CURLEC_TEST_KEY_ID and CURLEC_LIVE_KEY_ID in medusa-config.ts.

MYR enforced at the store level

apps/backend/src/scripts/enforce-myr.ts (dry run by default, MYR_APPLY=1 to write)

The seed left supported_currencies as EUR + USD. The admin offered those when creating price lists and promotions, so a price entered in EUR would save and then never apply to anything.

Vehicle fitment

Vehicle tags were normalised (apps/backend/src/scripts/normalise-vehicles.ts) and the storefront gained a sticky garage in src/modules/garage/, persisted to localStorage. Category pages fall back to unfiltered results when a vehicle filter would return nothing, rather than showing an empty grid.

updateProductTags(id, { metadata }) does not work — the DTO only carries value. Use updateProductTagsWorkflow from @medusajs/core-flows.

Gateway mode widget

apps/backend/src/admin/widgets/gateway-mode.tsx + apps/backend/src/api/admin/gateway-mode/route.ts

Read-only by design. A control that flipped live and test keys would be one stray click from either halting every payment or putting a real customer through a test gateway, and neither failure surfaces until settlements go missing. The endpoint returns the mode word only — never a key id, never a secret.

August 2026

Shopify migration

Products, variants, images and stock status imported. Weights for 4,404 variants via apps/backend/src/scripts/import-weights.ts.

product.thumbnail is stored separately from product.images. Updating images alone leaves the storefront showing the old host. Clear it with '', not null.

Shipping setup

apps/backend/src/scripts/setup-shipping-my.ts — creates the Jitra, Kedah stock location, service zone covering Malaysia, shipping profile and option.

Shipping options come back empty even with a valid cart and address unless the sales channel is linked to the stock location with linkSalesChannelsToStockLocationWorkflow. The script is step-wise idempotent because the first version bailed out early and left the setup half-built.

Deployment traps

Collected because each one cost a failed build or a wrong diagnosis:

TrapDetail
medusa develop skips typecheckBackend type errors only fail the Railway build. Run npm run build in apps/backend first.
Vercel needs a team scopeBare vercel --prod returns "Not authorized"; pass --scope vigor-digital-solution.
Vercel deploys from repo root onlyRunning vercel inside apps/storefront creates a junk project.
vercel env add defaults to sensitiveA sensitive NEXT_PUBLIC_* is unreadable at build time and silently uses the fallback.
Railway CLI token~/.railway/config.json has both token (null) and accessToken. Reading token yields Not Authorized, which looks like a permissions problem.
Custom domain targetPortCan be created as null, in which case Railway's edge returns Application not found and the ACME challenge can never validate. Compare against a working domain.
Admin renders blanknpm hoists React 19 over the admin's React 18; fixed by a Vite alias in medusa-config.ts.

Keeping these docs current

These docs are part of the work, not a write-up afterwards. When a change lands:

  1. Add a plain-language entry to What's new — what someone running the shop would notice, not what the code does.
  2. Add the engineering detail here, including any trap worth not rediscovering.
  3. Update the API reference if a route, payload or environment variable changed.
  4. Update the user guide if a screen or workflow moved.

A change that alters behaviour but ships no docs is unfinished. If a fix later turns out to be wrong, correct the original entry rather than appending a contradiction — a changelog that argues with itself is worse than none.

On this page