docs: add backend integration guide + implementation prompt for B2B Document how the B2B storefront sends/gets data vs main (base-URL resolution, bootstrap fetch, interceptor chain, headers) and the new builder/backoffice surface awaiting a real API. Add a self-contained hand-off prompt. Login and payments left untouched. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> @
8.3 KiB
Backend Integration — B2B branch
How the B2B frontend sends and gets data, and how that differs from main.
Scope excludes login/auth and payments — those contracts are frozen and untouched here.
Source of truth in code:
src/app/services/api.service.ts— storefront reads/writessrc/app/core/config/api-config.service.ts— base-URL resolutionsrc/app/interceptors/api-base-url.interceptor.ts+api-headers.interceptor.ts— request rewriting + headerssrc/app/core/config/config.service.ts+core/bootstrap/providers/*— bootstrap loadsrc/app/shared/models/config/*— bootstrap/endpoint models
1. What changed vs main (one screen)
| Concern | main |
B2B |
|---|---|---|
| API base URL | Hardcoded environment.apiUrl (/api) |
Resolved per-tenant at runtime by ApiConfigService |
| Multi-tenant | One build per brand (environment.lavero.ts, index.novo.html, …) |
Single build, tenant resolved from host; per-brand env/html files deleted |
| Config source | Compiled into environment.ts (brand, theme, logo, phones) |
Fetched at runtime via GET /bootstrap → BootstrapConfig |
| Data providers | ApiService called directly everywhere |
Same endpoints, now behind repository/provider interfaces + DI tokens (swap mock↔api without touching UI) |
| Mock strategy | mockDataInterceptor intercepts HTTP |
Provider/gateway pattern picks mock vs api per domain (RuntimeProviderStrategyService) |
| Interceptor chain | [mockData, apiHeaders, cache] |
[mockData, **apiBaseUrl**, apiHeaders, adminAuth, cache] |
| Storefront read/write endpoints | see §3 | unchanged in shape |
Bottom line for backend: the storefront read/write contract (categories, items, search, cart, reviews) is the same as main. What is new is (a) a GET /bootstrap document the frontend now depends on, and (b) two whole endpoint namespaces (builder, backoffice) that are declared but empty and served by client-side mocks today.
2. How a request is built (B2B pipeline)
Every storefront call goes through this chain:
- Code calls
this.http.get('/api/category')(or${apiConfig.getBaseUrl()}/category). apiBaseUrlInterceptor— if the URL starts with/api, rewrites it to the resolved tenant base viaApiConfigService.toApiUrl().apiHeadersInterceptor— on any API request, attaches:X-Region—Moscow/ST. Petersburg/Yerevan(from region id)X-Language—RU/EN/AMCurrency—RUB(default) /USD/AMDWebSessionID— 32-char hex; from auth session if present, else a persisted anonymous id (localStorage: web_session_id)
- Request goes out to the resolved absolute base.
Base-URL resolution (ApiConfigService.getBaseUrl)
Priority order:
- localhost →
environment.localhostApiUrl(/api, proxied by dev server) environment.tenantApiBaseUrls[tenantKey](e.g.default/dexarmarket→https://api.dexarmarket.ru:445)environment.tenantApiTemplate→https://{tenant}.api.dexarmarket.ru:445- (opt-in)
bootstrap.apiEndpoints.website.baseUrl/bootstrap.tenant.apiBaseUrl— only ifallowBootstrapApiOverride: trueand absolute - fallback
environment.apiUrl
Backend impact: for a new tenant, either add it to
tenantApiBaseUrls, or serve it athttps://<tenant>.api.dexarmarket.ru:445so the template matches. No frontend rebuild needed if the template host pattern holds.
3. GET — reads (storefront, real HTTP today)
Identical shapes to main. ApiService normalizes legacy and backOffice response formats (see normalizeItem/normalizeCategory — dual field names, 0x-hex colours, names[]→translations, imgs[]→photos, Go-typo tolerance like valuue).
| Method | Endpoint | Notes |
|---|---|---|
getCategories() |
GET /category |
retry ×2 backoff |
getCategoryItems(id,count,skip) |
GET /category/:id?count=&skip= |
|
getItem(id) |
GET /items/:id |
rating/reviews/questions derived from item |
searchItems(q,count,skip,opts) |
GET /searchitems?search=&count=&skip=&categoryIDs=&minPrice=&maxPrice=&tag=&sort= |
returns {items,total} |
getRandomItems(count,category?) |
GET /items/randomitems?count=&category= |
featured/related |
ping() |
GET /ping |
health |
GET /bootstrap — new, required in prod. Returns the full BootstrapConfig (tenant, branding, theme, company, featureFlags, apiEndpoints, localization, seo, permissions, navigation, footer, pages, staticPages, widgetRegistry). Loaded once, cached (ConfigService.loadBootstrap, shareReplay(1)). Shape: src/app/shared/models/config/bootstrap-config.model.ts; canonical example: src/assets/mock/bootstrap/bootstrap.json.
On localhost (or useMockData:true) the MockBootstrapProvider serves bootstrap.json instead of hitting /bootstrap, so the app runs fully offline (npm run dexar).
4. POST — writes (storefront, real HTTP today)
Unchanged from main (payment writes omitted per scope):
| Method | Endpoint | Body |
|---|---|---|
addToCart(sessionId, items) |
POST /websession/:sessionId |
[{itemID,quantity,colour?,size?,price?}] |
submitReview({...}) |
POST /items/:id/callback |
{rating,comment,sessionID,timestamp} |
submitQuestion({...}) |
POST /items/:id/questiion |
{question,sessionID,timestamp} — note existing path typo, keep it |
submitPurchaseEmail({...}) |
POST /purchase-email |
{email,phone?,telegramUserId,items[]} |
5. New backend surface the frontend is waiting on
Declared in the bootstrap model (apiEndpoints.builder, apiEndpoints.backoffice) but currently {} empty and served by localStorage / in-memory gateways. UI is done; wiring a real API = implement one gateway/provider against the existing interface and rebind its DI token — no page/component changes.
5a. Builder (Project Editor) — highest priority
The editor edits the same BootstrapConfig the storefront consumes. Today ProjectEditorIoService only JSON.stringifys it (export) — no HTTP, no persistence. Needed:
GET /builder/bootstrap/draft→ current draftBootstrapConfigPUT /builder/bootstrap/draft← save draftPOST /builder/bootstrap/publish→ promote draft to the liveGET /bootstrapdocumentPOST /builder/bootstrap/validate(optional) → server-side re-validation is mandatory; the client validator is not a trust boundary
5b. Backoffice (admin) — mock gateways awaiting real API
Each domain has a *Gateway/*Provider interface + a *LocalGateway mock bound via token. Implement *ApiGateway against the same interface, endpoints under /backoffice/..., models in features/admin/<area>/models/*.model.ts:
categories, products, orders, transactions, users/roles, dashboard metrics, monitoring feeds, media.
Provider selection is centralized in RuntimeProviderStrategyService (getBackofficeProviderMode, getProductProviderMode, getCategoryProviderMode): returns mock only when environment.useMockData, else api.
Full per-domain punch list and model shapes:
BACKEND.md§5–§16. Handoff framing:BACKEND-DIFF-VS-MAIN.md.
6. Contract rules for the backend
- Do not change existing storefront read/write shapes (categories, items, search, cart, reviews, questions) — frontend
mainandB2Bboth depend on them. - Serve
GET /bootstrapmatchingbootstrap-config.model.ts. Field-name tolerance exists on the client, but match the model to avoid normalization surprises. - Respect request headers
X-Region,X-Language,Currency,WebSessionIDfor localization/session-scoped responses. - New namespaces
builder/*andbackoffice/*are yours to define; keep them consistent with the declaredapiEndpointsblock so they can be published via bootstrap. - Out of scope here: auth (Telegram QR session) and payments (
/qr,/card,/cartpayment) — unchanged, see existing docs.
7. Known reliability issue (not fixable in frontend)
Intermittent 502/504 on refresh/back-nav in prod originate from the upstream reverse proxy at api.dexarmarket.ru:445 / users.vitanova.network:456 — the frontend calls those absolute URLs directly. Needs DevOps/backend investigation of upstream health/timeouts around bootstrap + session-check.