# BACKEND This is the punch list for backend engineers. Every item below is a frontend behavior that is currently **mocked or local-only**, with the exact gap, the endpoint(s) needed, and which frontend files change once the endpoint exists. Read this file instead of diffing the whole repo against `main`. General contract rules (apply to everything below): tenant is resolved by request host, never a path/query param (`docs/ARCHITECTURE.md`); bootstrap/config responses must never include secrets; existing auth/payment/authorization contracts are frozen (ADR-010) — nothing here changes them. ## 1. Auth — admin authorization gap (highest priority, security-relevant) **Current frontend behavior:** Admin login and customer login share **one** Telegram QR/session backend (`{authApiUrl}/users/sessions`) and **one** UI component (`TelegramLoginComponent`, `mode: 'customer' | 'admin'`). The only client-side difference is *where the resulting session id is stored*: `webSessionID` cookie (`SameSite=Lax`) for customers vs. `adminSessionID` cookie (`SameSite=Strict`) for admin, via separate `AuthService` / `AdminAuthService`. Admin API calls attach `AdminWebSessionID` via `adminAuthHeadersInterceptor` (`core/admin-auth/admin-auth-headers.interceptor.ts`). **The gap:** because both flows hit the identical Telegram session endpoint, the backend has **no concept of "this is an admin session"** at the moment the QR is scanned — it's an ordinary Telegram user session, indistinguishable from a customer's. The frontend only decides where to *store* the resulting id; it cannot and does not decide whether that Telegram user is actually allowed to act as an admin. Any Telegram user who completes the QR flow while the admin login UI is showing gets an `adminSessionID`. **What's needed:** server-side authorization check keyed off the session id (or a dedicated admin-scoped token) at the point admin API calls arrive with `AdminWebSessionID` — reject non-admin users. This must be enforced server-side; nothing on the frontend can substitute for it. **Frontend files that would change:** `src/app/core/admin-auth/admin-auth.service.ts`, `admin-auth-headers.interceptor.ts`, `admin-auth.guard.ts` — only if the auth response shape changes (e.g. a role claim to check client-side in addition to the server-side enforcement). **Also prepared, not wired:** `core/admin-auth/ed25519-verification.model.ts` defines `Ed25519VerificationService` (challenge/verify) for a future non-Telegram admin auth path. Current binding (`NoopEd25519VerificationService`, in `app.config.ts`) throws rather than silently accepting — safe to leave wired until a real challenge/verify endpoint exists. ## 2. Bootstrap draft / publish persistence (second-highest priority) **Current frontend behavior:** The Project Editor (`docs/EDITOR.md`) edits the same `BootstrapConfig` the storefront consumes. - Load: `GET /bootstrap` (existing, tenant by host). - Save: in-memory + `localStorage` only (`ProjectEditorDraftStorageService`, key `projectEditor.draftBootstrap.v1`, scoped by `tenant.id`). Reload in another browser/tab and it's gone. - Publish: applies the bootstrap in-memory via `PlatformRuntimeService.reloadFromBootstrap()` and flips a local `status` flag. **No backend call.** **Endpoints needed** (none exist yet; shapes are contracts, adjust to house conventions): ``` GET /builder/bootstrap/draft -> tenant's current draft BootstrapConfig. 404/empty if none (draft = published). PUT /builder/bootstrap/draft body: BootstrapConfig -> persists the draft for this tenant. Does not affect GET /bootstrap (storefront-facing). POST /builder/bootstrap/publish body: BootstrapConfig (or none, if publish always promotes the stored draft) -> validates, then makes this BootstrapConfig what GET /bootstrap returns. Only endpoint that affects the live storefront. POST /builder/bootstrap/validate (optional) body: BootstrapConfig -> { code: string, message: string }[], mirroring ProjectValidationIssue shape client-side. ``` Tenant identity: resolved by request host, same as every other endpoint — no `projectId` path param. One domain = one tenant = one draft = one published bootstrap. **Don't duplicate client validation, enforce it server-side too.** `ProjectValidator` (`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, non-hex `theme.palette` values, missing translations for a supported locale, and unknown layout/section-layout enum values. A malicious or stale client can bypass all of this — if `POST /builder/bootstrap/publish` is a trust boundary, re-run equivalent checks server-side. **Frontend files that would change:** `features/project-editor/facade/project-editor.facade.ts` (replace local save/publish with the new endpoints, keep the same public method signatures), `services/project-editor-draft-storage.service.ts` (becomes a fallback/offline cache rather than primary store). ## 3. Static pages / `slug` vs `route` inconsistency **Gap:** `StaticPageConfig` requires `slug: string`, but at least one real bootstrap fixture (`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, but the underlying data inconsistency remains. **Needed:** decide whether `slug` is backend-required or auto-derived from `route`, so both sides agree on one source of truth going forward. **Frontend files:** `shared/models/config/static-page.model.ts`, `features/content-management/services/*` (`ContentPageService` normalization), `features/project-editor/services/project-validator.service.ts`. ## 4. Static pages / CMS persistence **Current frontend behavior:** `staticPages` are edited client-side in the Project Editor (create/delete page, toggle footer/header/sitemap visibility, edit slug/icon/order/translations/HTML) and only ever written into the in-memory/`localStorage` bootstrap draft above (item 2) — no dedicated backend for CMS content exists. **Endpoints needed:** ``` GET /builder/content-pages PUT /builder/content-pages POST /builder/content-pages/import GET /builder/content-pages/export POST /builder/content-pages/validate ``` Backend must also support content moderation/validation on publish (disallow dangerous tags/attributes) and revision history — the frontend only sanitizes at render time, not at authoring time (`MarketplaceHtmlEditorComponent` emits raw HTML with no sanitization by design; sanitization is a storefront-render concern, not an authoring concern). **Frontend files:** `features/content-management/facade/*`, `services/*`, `components/html-editor/*`, `features/project-editor/sections/footer-section.component.ts` (static page list editing today lives partly here too). ## 5. Categories **Current behavior (Sprint 20):** `GET /category` (existing) still backs the public storefront via `CategoryDto -> CategoryMapper -> Category -> CategoryFacade`, unchanged. A full admin editor now exists at `features/admin/categories/` (list + create/edit, hierarchy, drag-and-drop reorder, soft delete/restore, draft/publish, SEO/translations — see `docs/ADMIN.md` "Sprint 20") but it runs entirely against `AdminCategoriesLocalGateway`, an in-memory cache seeded once from `BackofficeDataService.loadCategories()` (`CategoryCardConfig`, no hierarchy) — nothing persists across a page reload. **Gap:** no admin write path (create/update/delete/reorder categories) exists on the backend. `AdminCategory` also carries fields the current `CategoryDto`/`CategoryCardConfig` don't have yet: `parentId` (hierarchy), `slug`, `icon`, `imageUrl`, `status` (draft/published), `deletedAt` (soft delete), `seo`, per-locale `translations`. **Needed:** category CRUD endpoints matching the `AdminCategory` shape (`src/app/features/admin/categories/models/admin-category.model.ts`) plus a bulk reorder endpoint (`order` field) and a slug-uniqueness check (`GET /admin/categories/slug-taken?slug=...`, mirrors `AdminCategoriesGateway.isSlugTaken`). **Frontend files:** implement `AdminCategoriesApiGateway` against `AdminCategoriesGateway` (`services/admin-categories-gateway.interface.ts`) and rebind via an injection token (same swap pattern as `AdminDashboardMetricsGateway`/`BACKOFFICE_DATA_PROVIDER`) — the facade and pages don't change. Also still open: wire `admin/products`' category dropdown to `AdminCategoriesGateway` instead of its own `AdminProductsGateway.loadCategories()` (Sprint 21). ## 6. Products **Current behavior (Sprint 21):** `features/admin/products/` is fully built (list + editor pages) against `AdminProductsLocalGateway` (swappable via an injection token, same pattern as everywhere else) — i.e. it's ready for a real API gateway, but one has never been implemented. Sprint 21 added `barcode`, `archived` (soft archive/restore), `variants` (`{name, price, quantity}[]`), `relatedProductIds`, and wired the category dropdown to `AdminCategoriesGateway` (see item 5) instead of its own seed. **Needed:** product CRUD endpoints matching the current `AdminProduct` shape (`src/app/features/admin/products/models/admin-product.model.ts`) — `itemID`, `name`, `price`, `currency`, `categoryID`, `visible`, `archived`, `discount`, `images`, `badges`, `media`, `specifications`/`attributes`, `variants`, `relatedProductIds`, plus a bulk endpoint matching `PATCH /items/bulk`-style semantics for `applyBulkVisibility`/`applyBulkDelete`. **Frontend files:** implement `AdminProductsApiGateway` alongside the existing `AdminProductsLocalGateway` and rebind the injection token — `features/admin/products/pages/*` and the facade do not change. ## 7. Orders / revenue **Current behavior (Sprint 23):** `features/admin/orders/` now exists as an admin CRUD-ish surface (list/detail, status changes, refund request, cancel, notes, CSV export, print invoice) but runs entirely against `AdminOrdersLocalGateway`, which fabricates 24 synthetic in-memory orders — there is still no real order data anywhere in this system. The dashboard's Orders/Revenue cards (Sprint 19) still correctly render `pending-backend` rather than reading from this mock (they're intentionally not wired to it — the mock is order-management scaffolding, not a real metrics source). **Needed:** a real order domain — `AdminOrder` shape is in `src/app/features/admin/orders/models/admin-order.model.ts`. At minimum: order CRUD, status transitions with a timeline/audit trail, payment status, refund workflow, and a revenue aggregation endpoint for the dashboard cards. **Frontend files:** implement `AdminOrdersApiGateway` against `AdminOrdersGateway` (`services/admin-orders-gateway.interface.ts`) and rebind via an injection token — facade and pages don't change. ## 8. Dashboard metrics **Current behavior:** `AdminDashboardMetricsGateway` (token `ADMIN_DASHBOARD_METRICS_GATEWAY`) defaults to `AdminDashboardMetricsLocalGateway`, which composes `BackofficeDataService.loadCategories()/loadProducts()` client-side into counts. Everything else on the dashboard (marketplace status, theme, languages, last publish/save, bootstrap version, active layout, enabled widgets, system health) is derived from `ProjectEditorFacade` state, not a metrics endpoint. **Needed:** a `/builder/dashboard/summary`-style endpoint returning real-time counts and trend deltas. **Frontend files:** implement `AdminDashboardMetricsGateway` (real API version) and rebind the token in DI config — `AdminDashboardFacade` and all dashboard card components are unaffected (`docs/ADMIN.md` has the full architecture). ## 9. Recent Activity (admin dashboard) **Current behavior:** `AdminDashboardHistoryService` is `localStorage`-backed, scoped per tenant (`adminDashboard.activityHistory.v1`) — same limitation pattern as the draft storage in item 2. It will never show another editor's activity. **Needed:** a real audit-log endpoint (who changed what, when) that multiple admin sessions can read. **Frontend files:** `features/admin/dashboard/services/admin-dashboard-history.service.ts`, `facade/admin-dashboard.facade.ts`. ## 10. Translations **Current behavior:** all UI strings are static, compiled into the three locale files (`src/app/i18n/{en,ru,hy}.ts`) via the `Translations` interface (`translations.ts`) and served by `TranslateService`/`TranslatePipe`. There is no backend-editable translation surface — bootstrap-level translatable content (nav labels, static-page translations, footer copyright) is edited per-tenant through the Project Editor and stored in the bootstrap itself (see item 2), which is separate from the compiled UI-chrome strings. **Gap:** UI-chrome strings (button labels, section titles, validation messages) require a frontend deploy to change or add a locale — there's no backend-driven UI-string catalog. This may be acceptable (frontend chrome vs. tenant content are different concerns) but is worth an explicit decision if a backend team expects to control all copy. **Frontend files:** `src/app/i18n/*` (if this becomes backend-driven, it's a new i18n loading mechanism, a larger change than any other item here). ## 11. Search / autocomplete / trending (future-ready, not urgent) **Current behavior:** in-memory products/categories/tags power autocomplete and suggestions (`features/search/services/search-autocomplete.service.ts`); trending returns `null` (UI hides gracefully); search history is `LocalSearchHistoryRepository` (guest) with a `BackendSearchHistoryRepository` placeholder already defined but unimplemented. **Endpoints that would let this go live without frontend changes:** ``` GET /search/suggestions?q={term} GET /catalog/filters?category={id}&q={term} ``` Plus, if authenticated history/wishlist/compare/saved-searches sync is wanted: `GET/POST/DELETE /me/wishlist`, `/me/compare`, `/me/saved-searches`, `/me/recently-viewed`. **Frontend files:** `features/search/services/search-autocomplete.service.ts`, `search-trending.service.ts`, `search-history.repository.ts` (swap `LocalSearchHistoryRepository` for `BackendSearchHistoryRepository`) — facade/UI unaffected by design. ## 12. Product engagement (rating/reviews/questions) — partially real, confirm contract **Current behavior:** frontend already expects these endpoints and renders against them: `GET /products/{id}/rating`, `GET /products/{id}/reviews?page&pageSize`, `GET /products/{id}/questions?page&pageSize`, `POST /products/{id}/reviews` (rating/title/text/anonymous), `POST /products/{id}/questions` (text/anonymous). If these already exist, this is a contract-confirmation item, not new work; if not, they block the Reviews/Questions UI (`productPage.reviews`/`questions` config, already toggleable per tenant). **Frontend files:** none, if the contract matches what's documented in `docs/BOOTSTRAP.md`'s Product Engagement notes. --- ## 13. Transactions (Sprint 24, mock/local) **Current behavior:** `features/admin/transactions/` exists (list, retry-failed, fraud flag, per-transaction audit log, CSV export) against `AdminTransactionsLocalGateway`, which derives one synthetic transaction per seeded mock order from item 7's `AdminOrdersLocalGateway` — no real payment/transaction data exists. **Needed:** a real payments/transactions domain (card, QR, cash-on-delivery), linked to orders, with retry semantics matching whatever the actual payment provider supports, and fraud-flag persistence. **Frontend files:** implement `AdminTransactionsApiGateway` against `AdminTransactionsGateway` (`services/admin-transactions-gateway.interface.ts`) and rebind via an injection token. ## 14. Users, roles & permissions (Sprint 25, mock/local) **Current behavior:** `features/admin/users/` (users, built-in roles, invitations, per-user mock sessions, per-user audit log) against `AdminUsersLocalGateway` — fully synthetic, no backend. Passwordless login itself is real (`AdminAuthService`, Telegram QR, `docs/BACKEND.md` item 1) — only the roles/permissions/invitations/multi-session-listing layer on top is mocked. **Needed:** a real user/role domain tied to the eventual server-side admin-authorization enforcement in item 1 — role assignment, a real permission catalog, invitation emails, and genuine multi-device session listing (the current `AdminAuthService` only ever knows about the current browser's session). **Frontend files:** implement `AdminUsersApiGateway` against `AdminUsersGateway` (`services/admin-users-gateway.interface.ts`) and rebind via an injection token. ## 15. Monitoring (Sprint 26, mock/local except Health) **Current behavior:** `features/admin/monitoring/` — Health section reads real data (`AdminDashboardFacade.healthChecks`, unchanged from Sprint 19). Everything else (audit/security/login/failed-login/API/error/warning event feed, queue depths, webhook deliveries) is synthetic, seeded once in `AdminMonitoringLocalGateway` — no logging, queue, or webhook infrastructure exists anywhere in this system. **Needed:** real structured logging with a query API (by category/level/actor/time range), real queue introspection (whatever job runner ships), and real webhook delivery tracking once webhooks exist as a feature at all. **Frontend files:** implement `AdminMonitoringApiGateway`-equivalent methods against a to-be-defined `AdminMonitoringGateway` interface (`AdminMonitoringLocalGateway` currently has no interface extracted — add one when a real implementation is built, mirroring the pattern used everywhere else in `admin/*`). ## 16. Analytics - visitors/funnels/heatmaps (Sprint 27, no backend at all) **Current behavior:** `features/admin/analytics/` computes real revenue/orders/top-products aggregations from the existing mock order data (see item 7), but visitor traffic, conversion funnels, and heatmaps have zero data source anywhere in this system (no analytics/tracking pipeline, no event collection) — these render `pending-backend` badges rather than fabricated numbers. **Needed:** a traffic/event tracking pipeline (page views, sessions, conversion events) and a funnel/heatmap aggregation service, before this section of the Analytics page can show anything real. **Frontend files:** `features/admin/analytics/pages/admin-analytics-page.component.html` currently renders the pending-backend badge inline (no gateway method exists for this yet, unlike every other mocked domain in this doc). ## 17. Sitemap generation (Sprint 28, static baseline only) **Current behavior:** `public/sitemap.xml` (new, Sprint 28) lists only the statically-known top-level marketplace routes (home/catalog/search/wishlist/compare) for the default `ru` locale segment, referenced from `public/robots.txt`'s `Sitemap:` directive. **Gap:** this is a multi-tenant, config-driven platform (`docs/ARCHITECTURE.md`) — supported locales, categories, products, and static pages are all resolved at runtime from each tenant's bootstrap config, not enumerable from the frontend at build time. A real per-tenant sitemap covering `/:lang/product/:id`, `/:lang/catalog/:categoryId`, and `/:lang/:staticPath` needs a backend/build-time job that reads the same bootstrap data source (categories/products/static pages, items 5/6/3-4 above) per tenant/domain and regenerates or serves this file dynamically — not something the SPA can produce correctly on its own. **Needed:** a build-time or server-side sitemap generator with access to the real per-tenant product/category/static-page lists and domain, likely alongside whatever eventually serves `bootstrap.json` per-domain server-side (item 2). ## Known reliability issues ### Production 502/504 Bad Gateway on refresh / back-navigation **Symptom:** production hits intermittent `502`/`504 Bad Gateway` responses specifically on page refresh and browser back-navigation. **Root cause (investigation so far):** `environment.production.ts` points the frontend at the backend API via **absolute URLs directly** (`apiUrl: 'https://api.dexarmarket.ru:445'`, `authApiUrl: 'https://users.vitanova.network:456'`), bypassing this repo's `nginx.conf` entirely — that config only proxies `/api` for the `lovero.store` tenant, not `dexarmarket.ru`. So the 502/504 originates from **that backend API's own reverse proxy** (ports 445/456, a separate server not in this repo), not from anything this repo controls. Refresh and back-navigation both re-fire session-check and bootstrap-load calls on mount (`AdminAuthService.checkSession()`, `ConfigService.loadBootstrap()`, `TelegramSessionApiService`), which is the likely trigger if that backend's app server or reverse proxy is crashing, overloaded, or misconfigured on those specific endpoints. **This is not fixable from this repo.** It needs DevOps/backend investigation on the reverse proxy and app server fronting `api.dexarmarket.ru:445` and `users.vitanova.network:456` — check upstream health, timeout settings, and concurrent-connection handling around session-check and bootstrap endpoints.