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.
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 viaPlatformRuntimeService.reloadFromBootstrap(for live preview) and flips a localstatusflag 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.normalizeSlugagainst legacy-shaped static-page fixture data (frontend bug, not a backend concern).