Files
marketplaces/docs/Backend-Handoff-Sprint16.md
sdarbinyan c6482f0037
Some checks failed
Architecture Governance / architecture (push) Has been cancelled
docs: add nginx tenant onboarding template and backend handoff doc
Adds a copy-paste server block template for onboarding a new marketplace
domain, and a Sprint 16 backend handoff doc covering the still-missing
draft/publish persistence endpoints, server-side validation expectations,
and the slug/route inconsistency in static-page data.
2026-07-13 16:44:34 +04:00

4.3 KiB

Backend Handoff — Sprint 16 (Project Editor)

For backend devs picking up work after the Sprint 16 frontend editor. Frontend is done; this documents what backend still needs to build for the editor to be real (not just an in-browser demo).

What exists today (frontend-only)

The Project Editor (/edit/:section, or /{lang}/edit/:section) edits the same BootstrapConfig the storefront consumes — no parallel model. Today:

  • Load: GET /bootstrap (existing, tenant resolved by request host).
  • Save: in-memory only. ProjectEditorFacade.save() just snapshots the current draft as "last saved" in the browser tab. Nothing is persisted. Reload the page, or open the editor in another tab/browser, and it's gone.
  • Publish: ProjectEditorFacade.publish() applies the bootstrap in-memory via PlatformRuntimeService.reloadFromBootstrap (for live preview) and flips a local status flag to 'published'. It does not call any backend endpoint. Nothing is persisted.

This is fine for demoing the editor UI to one person in one browser tab. It is not usable as a real per-tenant admin panel yet — that's this handoff.

Endpoints backend needs to add

None of these exist yet. Suggested shapes (adjust to match your existing API conventions — these are contracts, not prescriptions):

GET  /builder/bootstrap/draft
  -> returns the tenant's current draft BootstrapConfig (may differ from
     the published one). 404/empty if no draft exists yet (draft = published).

PUT  /builder/bootstrap/draft
  body: BootstrapConfig
  -> persists the draft for this tenant. Does not affect what GET /bootstrap
     (storefront-facing) returns.

POST /builder/bootstrap/publish
  body: BootstrapConfig  (or no body, if publish always promotes the
        current stored draft)
  -> validates, then makes this BootstrapConfig the one GET /bootstrap
     returns for this tenant. This is the only endpoint that affects the
     live storefront.

POST /builder/bootstrap/validate   (optional — validation already runs
                                     client-side via ProjectValidator, but a
                                     server-side check prevents a stale/
                                     bypassed client from publishing garbage)
  body: BootstrapConfig
  -> returns the same shape as the client's ProjectValidationIssue[]:
     { code: string, message: string }[]

Tenant identity: same as every other endpoint in this platform — resolved by request host, not a projectId path param (see docs/backend-platform/ tenant-resolution.md). There is no multi-project-per-domain concept; each domain is one tenant with one draft and one published bootstrap.

What the frontend already validates (don't duplicate logic, just enforce it)

ProjectValidator (src/app/features/project-editor/services/ project-validator.service.ts) blocks Publish client-side on: missing branding.logoUrl, empty localization.supportedLocales, invalid tenant.websiteBaseUrl (must be http(s)://...), duplicate static-page identifiers (slug, falling back to route), empty homepage sections, a homepage widget with no type, duplicate header nav links, and any non-hex-string theme.palette value. A malicious or buggy client could bypass all of this — if POST /builder/bootstrap/publish is meant to be a trust boundary, re-run equivalent checks server-side before accepting.

Static pages: slug vs route

Heads up for whoever owns StaticPageConfig: the model requires slug: string, but at least one real bootstrap in this repo (src/assets/mock/bootstrap/bootstrap.json) only populates route (e.g. /about-us) and leaves slug undefined. The frontend's duplicate-detection was patched to fall back to route when slug is empty (project-validator.service.ts), but the underlying data inconsistency is still there. Worth deciding whether slug should be backend-required/ auto-derived from route going forward, so both frontend and backend agree on one source of truth.

Not in scope for this handoff (already tracked separately)

  • Separate admin app/deployment (admin.<domain> subdomain) — storefront and editor still ship in one Angular build today.
  • A pre-existing, unrelated crash in ContentPageService.normalizeSlug against legacy-shaped static-page fixture data (frontend bug, not a backend concern).