diff --git a/docs/context/BACKEND-AUDIT.md b/docs/context/BACKEND-AUDIT.md new file mode 100644 index 0000000..7db5e87 --- /dev/null +++ b/docs/context/BACKEND-AUDIT.md @@ -0,0 +1,823 @@ +# Backend Surface Audit + +Machine-oriented, exhaustive audit of every backend touch-point the Angular frontend +expects — derived from the current source tree on branch `B2B`, not copied from prior +docs. Purpose: single input for downstream backend-integration documentation tasks. + +Legend for maturity (mirrors `docs/BACKEND_API.md`'s tagging so the two stay reconcilable): + +- **LIVE** — real `HttpClient` call exists in code today (file cited). +- **MOCK-SWAPPABLE** — interface + mock implementation exist, wired through an Angular + DI token so a real `*Api*` class can be dropped in without touching UI. A real impl + may or may not exist yet. +- **MOCK-ONLY (no seam)** — mock/local implementation exists but the facade injects the + concrete local class **directly** (no DI token). Adding a backend here first requires + introducing a token seam. This is the single most important structural finding below. +- **LOCAL-ONLY** — never talks to a backend by design (localStorage / in-memory / + derived from already-loaded bootstrap). Listed for completeness. + +## Table of contents + +1. [Executive summary & key findings](#1-executive-summary--key-findings) +2. [Runtime provider strategy & environment](#2-runtime-provider-strategy--environment) +3. [HTTP interceptor pipeline](#3-http-interceptor-pipeline) +4. [Live HTTP endpoints (verified in code)](#4-live-http-endpoints-verified-in-code) +5. [Domain: Auth (customer + admin)](#5-domain-auth-customer--admin) +6. [Domain: Bootstrap / config / tenant](#6-domain-bootstrap--config--tenant) +7. [Domain: Products & catalog](#7-domain-products--catalog) +8. [Domain: Categories](#8-domain-categories) +9. [Domain: Backoffice storefront data](#9-domain-backoffice-storefront-data) +10. [Domain: Cart / orders / payments](#10-domain-cart--orders--payments) +11. [Domain: Reviews & questions (engagement)](#11-domain-reviews--questions-engagement) +12. [Domain: Location / regions](#12-domain-location--regions) +13. [Domain: Widgets / dynamic renderer](#13-domain-widgets--dynamic-renderer) +14. [Admin gateways (feature area)](#14-admin-gateways-feature-area) +15. [Domain: Media library](#15-domain-media-library) +16. [Domain: Content management / static pages](#16-domain-content-management--static-pages) +17. [Domain: Project editor / builder](#17-domain-project-editor--builder) +18. [Domain: Search](#18-domain-search) +19. [Domain: User experience (wishlist/compare/etc.)](#19-domain-user-experience-wishlistcomparetc) +20. [Domain: Diagnostics](#20-domain-diagnostics) +21. [Facade catalog](#21-facade-catalog) +22. [Gateway / provider master table](#22-gateway--provider-master-table) +23. [Model / DTO catalog](#23-model--dto-catalog) +24. [Endpoint URL literals found in code](#24-endpoint-url-literals-found-in-code) +25. [Cross-check against existing docs](#25-cross-check-against-existing-docs) + +--- + +## 1. Executive summary & key findings + +- **~9 real HTTP-speaking domains** exist today: product/catalog, categories, cart/orders/ + payments, reviews/questions, telegram session auth, bootstrap, backoffice storefront data, + widget manifest, location/regions. Plus **Ed25519 admin auth** — wired to real `HttpClient` + but the endpoints are not implemented server-side yet (calls 404 today, by design). +- **Two provider seams are token-bound and API-ready today**: `PRODUCT_DATA_PROVIDER` + (→ `ApiProductDataProvider`, LIVE) and `CATEGORY_REPOSITORY` (→ `ApiCategoryRepository`, LIVE), + plus `CONFIG_PROVIDER` and `BACKOFFICE_DATA_PROVIDER` which switch mock↔api by strategy. +- **KEY STRUCTURAL FINDING — most admin CRUD domains have no swap seam.** Of the 11 admin + gateway domains, only **categories** (`ADMIN_CATEGORIES_GATEWAY`) and **dashboard-metrics** + (`ADMIN_DASHBOARD_METRICS_GATEWAY`) are injected via DI token. The other 9 (orders, products, + users, transactions, monitoring, moderation, customers, analytics, and the products/orders + gateways reused by analytics/customers) have their facades inject the concrete + `Admin*LocalGateway` **class directly**. A backend engineer cannot "just rebind a token" for + those — a token must be introduced first. This partially contradicts the blanket + "PLANNED / rebind the token" framing in `docs/BACKEND_API.md`. +- **Only one real `*Api*Gateway` exists in the admin area**: `AdminCategoriesApiGateway` + (`src/app/features/admin/categories/services/admin-categories-api.gateway.ts`). Every other + admin domain is local-mock only. +- **Media** is bound by class token (`MediaRepository` abstract class → `MockMediaRepository` + via `app.config.ts`), so it is MOCK-SWAPPABLE but no real impl exists. +- **Content-management and project-editor never hit a dedicated backend** — they read/mutate + the in-memory `BootstrapConfig` (loaded once from `GET /bootstrap`) and persist drafts to + localStorage. Publishing a marketplace = writing bootstrap back, for which no client write + call exists yet (LOCAL-ONLY today; a builder publish endpoint is FUTURE). +- **Two API base URLs are in play**: the tenant marketplace API (`ApiConfigService.getBaseUrl()`, + default `https://api.dexarmarket.ru:445`, `/api` on localhost) and a separate payment/QR API + (`environment.qrApiUrl` = `https://qr.vitanova.network/api`). Auth session API uses + `environment.authApiUrl` (= `https://api.dexarmarket.ru:445`). + +--- + +## 2. Runtime provider strategy & environment + +`src/app/core/providers/runtime-provider-strategy.service.ts` — `RuntimeProviderStrategyService` +decides mock vs api per domain. Modes: `'mock' | 'api' | 'remote-config'`. + +| Method | Returns `mock` when | Else | +|---|---|---| +| `getBootstrapProviderMode()` | `useMockData` true, OR `useMockBootstrapOnLocal && isLocalhost()` | `api` | +| `getBackofficeProviderMode()` | `useMockData` true | `api` | +| `getProductProviderMode()` | `useMockData` true | `api` (mock/remote-config fall through to api in token factory) | +| `getCategoryProviderMode()` | `useMockData` true, OR `useMockBootstrapOnLocal && isLocalhost()` | `api` | + +Note: `PRODUCT_DATA_PROVIDER` and `CATEGORY_REPOSITORY` token factories currently return the +**Api** provider for every mode (the `case 'mock'` falls through) — there is no mock product/ +category provider class bound. `getCategoryProviderMode()` returning `mock` only matters for +`ADMIN_CATEGORIES_GATEWAY` (which does honor it → `AdminCategoriesLocalGateway`). + +`src/environments/environment.ts` relevant keys: + +``` +useMockData: false +useMockBootstrapOnLocal: true +allowBootstrapApiOverride: false +localhostApiUrl: '/api' +tenantApiTemplate: 'https://{tenant}.api.dexarmarket.ru:445' +tenantApiBaseUrls: { default: 'https://api.dexarmarket.ru:445', dexarmarket: 'https://api.dexarmarket.ru:445' } +apiUrl: '/api' +authApiUrl: 'https://api.dexarmarket.ru:445' +qrApiUrl: 'https://qr.vitanova.network/api' +telegramBot: 'myAMLKYCBOT' (fallback in code: 'DexarSupport_bot') +``` + +`src/app/core/config/api-config.service.ts` — `ApiConfigService.getBaseUrl()` resolves the +tenant marketplace API base: localhost → `localhostApiUrl`; else `tenantApiBaseUrls[tenantKey]`; +else `tenantApiTemplate` with `{tenant}` substituted; else optional bootstrap override +(gated by `allowBootstrapApiOverride`, reads `bootstrap.apiEndpoints.website.baseUrl` / +`bootstrap.tenant.apiBaseUrl`). `isApiRequest(url)` = starts with `/api` or the base URL. +`toApiUrl(url)` rewrites a `/api`-prefixed relative URL onto the resolved base. + +Tenant key comes from `TenantResolverService` (`src/app/core/config/tenant-resolver.service.ts`). + +--- + +## 3. HTTP interceptor pipeline + +Registered in `src/app/app.config.ts` in this order: + +``` +withInterceptors([mockDataInterceptor, apiBaseUrlInterceptor, apiHeadersInterceptor, adminAuthHeadersInterceptor, cacheInterceptor]) +``` + +| Interceptor | File | Responsibility | +|---|---|---| +| `mockDataInterceptor` | `src/app/interceptors/mock-data.interceptor.ts` | When `environment.useMockData`, short-circuits marketplace endpoints with in-memory fixtures (categories, items, search, cart, qr, callback, purchase-email, sessions). Matches URL patterns — see §24. | +| `apiBaseUrlInterceptor` | `src/app/interceptors/api-base-url.interceptor.ts` | Rewrites `/api/*` relative URLs to `ApiConfigService.toApiUrl()`. | +| `apiHeadersInterceptor` | `src/app/interceptors/api-headers.interceptor.ts` | For marketplace API requests, sets headers: `X-Region`, `X-Language` (RU/EN/AM), `Currency` (default RUB), `WebSessionID` (auth session id or persisted anonymous 32-hex id in localStorage key `web_session_id`). | +| `adminAuthHeadersInterceptor` | `src/app/core/admin-auth/admin-auth-headers.interceptor.ts` | For requests whose URL contains `/admin/`, `/backoffice/`, `/builder/`, `/media/`, sets `AdminWebSessionID` header (from `AdminAuthService.session()`) and `Authorization: Bearer ` if an admin token is stored. | +| `cacheInterceptor` | `src/app/interceptors/cache.interceptor.ts` | Client-side GET response caching. | + +Header value maps (from `apiHeadersInterceptor`): language `ru→RU, en→EN, hy→AM`; +region `moscow→Moscow, spb→ST. Petersburg, yerevan→Yerevan`. + +--- + +## 4. Live HTTP endpoints (verified in code) + +All paths relative to `ApiConfigService.getBaseUrl()` unless a full origin is shown. Payment +endpoints use `environment.qrApiUrl`; session endpoints use `environment.authApiUrl`. + +### Marketplace API — `src/app/services/api.service.ts` (`ApiService`) + +| Method | HTTP | Path | Notes | +|---|---|---|---| +| `ping()` | GET | `/ping` | `{ message }` | +| `getCategories()` | GET | `/category` | normalized to `Category[]` | +| `getCategoryItems(id,count,skip)` | GET | `/category/{categoryID}?count&skip` | `Item[]` | +| `getItem(id)` | GET | `/items/{itemID}` | single `Item` | +| `searchItems(search,count,skip,opts)` | GET | `/searchitems?search&count&skip[&categoryIDs&minPrice&maxPrice&tag&sort]` | `{ items, total }` | +| `getRandomItems(count,categoryID?)` | GET | `/items/randomitems?count[&category]` | `Item[]` (featured) | +| `addToCart(sessionId,items)` | POST | `/websession/{sessionId}` | body = item array | +| `submitReview(data)` | POST | `/items/{itemID}/callback` | body: rating, comment, sessionID, timestamp | +| `submitQuestion(data)` | POST | `/items/{itemID}/questiion` | **NOTE: literal typo `questiion`** matches backend spec | +| `createCartPayment(payload)` | POST | `/cart` | `CartPaymentRequest` → `QrCreateResponse` | +| `createOrder(payload)` | POST | `/orders` | `CreateOrderRequest` → `CreateOrderResponse`; fire-and-forget after payment | +| `submitPurchaseEmail(data)` | POST | `/purchase-email` | email receipt | +| `createPayment(payload,headers)` | POST | `{qrApiUrl}/qr` | headers `authorization-key`, `userid-value` | +| `checkCartPaymentStatus(qrId)` | GET | `{qrApiUrl}/qr/dynamic/{partnerId}/{qrId}` | partnerId const `web-97ec-9c57-4dde-9037-3a68f7f83750` | +| `checkCartCardPaymentStatus(orderId)` | GET | `{qrApiUrl}/card/{partnerId}/{orderId}` | | +| `checkPaymentStatus(partnerQrId,qrId)` | GET | `{qrApiUrl}/qr/dynamic/{partnerQrId}/{qrId}` | | + +Also builds an external QR image URL (`https://api.qrserver.com/v1/create-qr-code/...`) — not a backend of this platform. + +### Other live callers + +| Caller (file) | HTTP | Path | Base | +|---|---|---|---| +| `ApiHealthService` (`src/app/services/api-health.service.ts`) | GET | `/ping` | marketplace base | +| `ApiCategoryRepository` (`src/app/core/categories/repositories/api-category.repository.ts`) | GET | `/category` | marketplace base; retry x2 | +| `ApiBootstrapProvider` (`src/app/core/bootstrap/providers/api-bootstrap.provider.ts`) | GET | `/bootstrap` | relative | +| `MockBootstrapProvider` | GET | `/assets/mock/bootstrap/bootstrap.json` | static asset | +| `ApiBackofficeDataProvider` (`src/app/core/backoffice/providers/api-backoffice-data.provider.ts`) | GET | `/api/backoffice/products`, `/api/backoffice/categories` | | +| `WidgetManifestService` (`src/app/widgets/registry/widget-manifest.service.ts`) | GET | `bootstrap.widgetRegistry.manifestUrl` or `/assets/mock/bootstrap/widget-manifest.json` | | +| `LocationService` (`src/app/services/location.service.ts`) | GET | `/regions` (marketplace base); `http://ip-api.com/json/...` (external geo-IP) | | +| `TelegramSessionApiService` (`src/app/services/telegram-session-api.service.ts`) | POST/GET/DELETE | `{authApiUrl}/users/sessions`, `/users/sessions/{id}` | session auth | +| `AuthApiService` (`src/app/core/auth/services/auth-api.service.ts`) | GET/POST | `{authApiUrl}/api/admin/auth/challenge|verify|refresh|logout` | **not implemented server-side yet** | +| `ApiProductDataProvider` | (delegates to `ApiService`) | see above | | + +--- + +## 5. Domain: Auth (customer + admin) + +Two distinct auth mechanisms coexist. + +### 5a. Telegram session auth (LIVE) — customer AND admin + +`src/app/services/telegram-session-api.service.ts` — `TelegramSessionApiService`. Single source +for both customer (`AuthService`) and admin (`AdminAuthService`) login; there is no separate +admin backend endpoint. Only storage is kept separate (distinct cookie/signals). + +| Method | HTTP | Path | Request | Response (normalized) | +|---|---|---|---|---| +| `createSession()` | POST | `{authApiUrl}/users/sessions` | `{ webSessionID }` + header `WebSessionID` | `WebSessionStart { webSessionID, url }` (url = `https://t.me/{bot}?start={id}`) | +| `checkSessionOnce(id)` | GET | `{authApiUrl}/users/sessions/{id}` | — | `AuthSession | null` (heavily field-tolerant normalizer) | +| `logout(id)` | DELETE | `{authApiUrl}/users/sessions/{id}` | header `WebSessionID` | ignored | + +Consumers: `AuthService` (`src/app/services/auth.service.ts`, customer), +`AdminAuthService` (`src/app/core/admin-auth/admin-auth.service.ts`, admin — separate cookie +`adminSessionID`, has dev-only `devBypassLogin()`), `AuthFacade` +(`src/app/core/auth/services/auth-facade.service.ts`) wrapping AuthService/SessionService/ +PermissionService for components. Also `src/app/shared/qr-login/`. + +Models: `AuthSession`, `WebSessionStart`, `AuthStatus` (`src/app/models/auth.model.ts`); +`AdminAuthStatus` (`src/app/models/admin-auth.model.ts`). + +### 5b. Ed25519 challenge/response admin auth (LIVE wiring, backend absent) + +`src/app/core/auth/services/auth-api.service.ts` — `AuthApiService`. Real `HttpClient` wiring +against a documented contract that the backend has NOT implemented yet (calls 404 today, +mapped to a `backend-unavailable` error screen). No mocks fabricated. + +| Method | HTTP | Path (`{authApiUrl}/api/admin/auth`) | Request | Response | +|---|---|---|---|---| +| `requestChallenge()` | GET | `/challenge` | — | `AuthChallenge { nonce, issuedAt, expiresAt }` | +| `verifySignature(req)` | POST | `/verify` | `VerifySignatureRequest { publicKey, signature, nonce }` | `AuthTokenPair { token, refreshToken }` | +| `refresh(req)` | POST | `/refresh` | `RefreshTokenRequest { refreshToken }` | `AuthTokenPair` | +| `logout(refreshToken)` | POST | `/logout` | `{ refreshToken }` | void | + +Models: `src/app/core/auth/models/auth-api.model.ts` (`AuthChallenge`, `VerifySignatureRequest`, +`AuthTokenPair`, `RefreshTokenRequest`, `JwtClaims`). Supporting: +`src/app/core/auth/services/ed25519-keypair.service.ts` (keypair gen/signing); +`src/app/core/admin-auth/ed25519-verification.model.ts` + +`noop-ed25519-verification.service.ts` (bound in `app.config.ts` via +`{ provide: Ed25519VerificationService, useClass: NoopEd25519VerificationService }`). + +Permissions/roles: `src/app/core/auth/models/permission.model.ts` — `AdminRole` +(`Owner|Administrator|Editor|Support|ReadOnly`), `Permission` union. +Errors: `src/app/core/auth/models/auth-error.model.ts` — `AuthErrorCode`, `AuthError`. +Guards: `src/app/core/admin-auth/admin-auth.guard.ts`, `src/app/guards/**`. + +--- + +## 6. Domain: Bootstrap / config / tenant + +The runtime configuration document that drives the entire multi-tenant platform. + +- **Contract interface**: `ConfigProvider` (`src/app/core/config/config-provider.interface.ts`) + — `loadBootstrap(): Observable`. +- **DI token**: `CONFIG_PROVIDER` (`src/app/core/config/config-provider.token.ts`), factory + switches on `getBootstrapProviderMode()`: `mock` → `MockBootstrapProvider` + (`/assets/mock/bootstrap/bootstrap.json`), else → `ApiBootstrapProvider` (`GET /bootstrap`). +- **Implementations**: `ApiBootstrapProvider`, `MockBootstrapProvider` + (`src/app/core/bootstrap/providers/*`). +- **Consuming services**: `ConfigService` (`src/app/core/config/config.service.ts`, + holds the bootstrap snapshot), `ApiConfigService`, `FeatureConfigService`, + `TenantResolverService`, `FooterResolverService`, `StaticPageResolverService` + (all `src/app/core/config/*`). +- **Consuming facades**: `UiRuntimeFacade` (`src/app/facades/runtime/ui-runtime.facade.ts`), + `WebsiteRuntimeFacade` (`src/app/facades/website/website-runtime.facade.ts`), + `ProjectEditorFacade`, `ContentManagementFacade`, `DiagnosticsFacade`. + +`BootstrapConfig` (`src/app/shared/models/config/bootstrap-config.model.ts`) aggregates ~24 +sub-configs, each its own file under `src/app/shared/models/config/`: + +`schemaVersion, generatedAt, tenant, branding, theme, company, featureFlags, features?, +apiEndpoints, localization, seo, permissions, header?, catalog?, layout?, navigation, footer?, +productPage?, userExperience?, pages[], staticPages?, widgetRegistry?` + +Sub-config model files (all backend-shaped, served inside bootstrap): +`api-endpoints.model.ts` (`ApiEndpointConfig{path,method,timeoutMs?}`, `ApiEndpointsConfig{ +bootstrap, website:Record<...>, builder:Record<...>, backoffice:Record<...>}`), +`tenant.model.ts` (`TenantConfig{id,slug,code,host,name,websiteBaseUrl,builderBaseUrl, +backofficeBaseUrl,defaultLocale,supportedLocales,defaultCurrency,supportedCurrencies,timezone}`), +`branding.model.ts`, `theme.model.ts`, `company.model.ts`, `feature-flags.model.ts`, +`features-config.model.ts`, `footer-config.model.ts`, `header-config.model.ts`, `layout.model.ts`, +`localization.model.ts`, `navigation.model.ts`, `page.model.ts`, `permissions.model.ts`, +`product-page-config.model.ts`, `catalog-config.model.ts`, `seo.model.ts`, +`static-page.model.ts`, `user-experience-config.model.ts`, `widget-registry.model.ts`, +`widget.model.ts`, `section.model.ts`. Barrel: `src/app/shared/models/config/index.ts`. + +`ApiEndpointsConfig.website/builder/backoffice` are `Record` — +i.e. the bootstrap document is where a tenant's PLANNED endpoint paths are declared at runtime. +No literal builder/backoffice path constants exist in code (see §24). + +--- + +## 7. Domain: Products & catalog + +- **Contract interface**: `ProductDataProvider` + (`src/app/core/products/providers/product-data-provider.interface.ts`). +- **DI token**: `PRODUCT_DATA_PROVIDER` (`src/app/core/products/product-data-provider.token.ts`) + — factory returns `ApiProductDataProvider` for all modes (no mock provider class bound). +- **Real impl (LIVE)**: `ApiProductDataProvider` + (`src/app/core/products/providers/api-product-data.provider.ts`) — delegates to `ApiService` + + `CategoryService`; contains inline mapping (item→reviews/questions/rating summary). +- **Domain service**: `ProductDataService` (`src/app/core/products/product-data.service.ts`) + injected by `ProductFacade`. +- **Consuming facade**: `ProductFacade` (`src/app/facades/platform/product.facade.ts`). +- **Consuming components**: `catalog-container.component.ts`, + `product-details-container.component.ts` (`src/app/features/website/**`), home/catalog pages. + +Interface methods: `getProducts(query?)`, `getProduct(productID)`, `getCategories()`, +`searchProducts(query)`, `getFeaturedProducts(query?)`, `getLatestProducts(query?)`, +`getProductsByCategory(categoryID,query?)`, `getRelatedProducts(query)`, `loadRating(productID)`, +`loadReviews(productID,query?)`, `loadQuestions(productID,query?)`, `submitReview(productID,input)`, +`submitQuestion(productID,input)`. + +Models — `src/app/core/products/models/`: +- `product-domain.model.ts`: `Product = Item` (alias), `ProductCategory = Category`, + `ProductSort`, `ProductFilters`, `ProductListQuery`, `ProductSearchQuery`, `ProductListResult`, + `RelatedProductsQuery`, `RelatedProductCollection`, `ProductVariantSelection`. +- `product-engagement.model.ts`: `RatingStars`, `RatingDistributionEntry`, `RatingSummary`, + `Review`, `Answer`, `Question`, `EngagementListQuery`, `EngagementListResult`, + `SubmitReviewInput`, `SubmitQuestionInput`. +- `catalog-experience.model.ts`: `SearchCriteria`, `FilterDefinition`, `FilterOption`, + `SortDefinition`, `CatalogView`, `SearchResult`, layout/nav mode types. + +The **backend-shaped** product DTO is `Item` (`src/app/models/item.model.ts`) — the raw wire +shape. `ApiService.normalizeItem()` is the adapter: it reconciles legacy marketplace format and +newer backOffice format (string `id`↔numeric `itemID`, `imgs[]`↔`photos[]`, `names[]`↔ +`translations`, `itemDetails[]`, `description` key/value array↔string, `comments`↔`callbacks`, +`specificationGroups`, `variantOptions`, `relatedCollections`, delivery normalization, color +`0xRRGGBB`→`#RRGGBB`, remaining→stock band). This is the single largest inline mapper in the +codebase — a backend engineer should treat `normalizeItem`/`normalizeCategory` as the tolerance +contract. `Item` supporting types: `ProductMedia`, `DescriptionField`, `ItemName`, +`ProductSpecificationField/Group`, `ProductVariantOption(Group)`, `RelatedProductCollection`, +`DeliveryOption`, `ItemDetail`, `CartItem`. + +--- + +## 8. Domain: Categories + +Two parallel category stacks exist (legacy + clean-architecture): + +**Clean stack (MOCK-SWAPPABLE, real impl LIVE):** +- **Interface**: `CategoryRepository` (`src/app/core/categories/repositories/category.repository.ts`) + — `getCategories(): Observable`. +- **DI token**: `CATEGORY_REPOSITORY` (`src/app/core/categories/category-repository.token.ts`) + → `ApiCategoryRepository` for all modes. +- **Real impl (LIVE)**: `ApiCategoryRepository` — `GET /category`, retry x2. +- **DTO**: `CategoryDto`, `CategoryNameDto` (`src/app/core/categories/dto/category.dto.ts`). +- **Adapter**: `CategoryMapper` (`src/app/core/categories/mappers/category.mapper.ts`) — + `CategoryDto → Category` domain (flattens subcategory tree, dedupes by id, language + normalization `am→hy`). +- **Domain model**: `Category`, `CategoryTranslation` + (`src/app/core/categories/models/category-domain.model.ts`). +- **Facade**: `CategoryFacade` (`src/app/facades/platform/category.facade.ts`) via + `CategoryService` (`src/app/core/categories/category.service.ts`). Utils: + `category-tree.utils.ts`. + +**Legacy stack**: `ApiService.getCategories()` → `Category` (`src/app/models/category.model.ts`, +with `Subcategory`) via `normalizeCategory()`. Used by `ApiProductDataProvider.getCategories()`. +Note: two different `Category` types exist (`src/app/models/category.model.ts` vs +`src/app/core/categories/models/category-domain.model.ts`) — a known duplication. + +--- + +## 9. Domain: Backoffice storefront data + +Storefront-facing "cards" data (distinct from the admin/backoffice feature area). + +- **Interface**: `BackofficeDataProvider` + (`src/app/core/backoffice/providers/backoffice-data-provider.interface.ts`) — + `loadProducts(): Observable`, `loadCategories(): Observable`. +- **DI token**: `BACKOFFICE_DATA_PROVIDER` (`src/app/core/backoffice/backoffice-data-provider.token.ts`) + — `mock` → `MockBackofficeDataProvider`, else → `ApiBackofficeDataProvider`. +- **Impls**: `ApiBackofficeDataProvider` (LIVE, `GET /api/backoffice/products`, + `GET /api/backoffice/categories`), `MockBackofficeDataProvider` + (`src/app/core/backoffice/providers/*`). +- **Models**: `ProductCardConfig` (`src/app/shared/models/ui/product-card.model.ts`), + `CategoryCardConfig` (`src/app/shared/models/ui/category-card.model.ts`), + `ButtonConfig` (`button.model.ts`). Barrel: `src/app/shared/models/ui/index.ts`. + +--- + +## 10. Domain: Cart / orders / payments + +Cart state is **LOCAL-ONLY** but checkout produces LIVE payment/order calls. + +- **`CartService`** (`src/app/services/cart.service.ts`) — signal-based cart, persisted to + localStorage key `marketplace_cart` (+ Telegram CloudStorage when in Telegram WebApp). No + backend for cart contents. Models: `CartItem` (extends `Item`), `DeliveryOption`. +- **Checkout → `ApiService`** (see §4): `POST /cart` (`CartPaymentRequest`), + `POST /orders` (`CreateOrderRequest`→`CreateOrderResponse`), `POST /purchase-email`, + QR/card status polling on `qrApiUrl`. +- Request/response DTOs live inline in `api.service.ts`: `QrCreateRequest`, `QrCreateResponse`, + `CartPaymentRequest`, `CreateOrderRequest`, `CreateOrderResponse`, `QrDynamicStatusResponse`. +- Admin-side order/transaction views are a **separate** mock domain — see §14. + +--- + +## 11. Domain: Reviews & questions (engagement) + +Customer-facing. LIVE via `ApiService`. Interface methods on `ProductDataProvider`: +`loadRating`, `loadReviews`, `loadQuestions`, `submitReview`, `submitQuestion`. +Endpoints: `POST /items/{id}/callback` (review), `POST /items/{id}/questiion` (question, typo +preserved). Reads derive reviews/questions/rating from `GET /items/{id}` payload (no dedicated +list endpoints yet). Models in `product-engagement.model.ts` (§7). Admin **moderation** of +reviews/reports is a separate mock domain — see §14. + +--- + +## 12. Domain: Location / regions + +`LocationService` (`src/app/services/location.service.ts`), LIVE: +- `GET /regions` (marketplace base) → `Region[]`; falls back to 6 hardcoded regions on error. +- `GET http://ip-api.com/json/?fields=...` (external geo-IP, no key) for auto-detect. +Models: `Region`, `GeoIpResponse` (`src/app/models/location.model.ts`). Region id feeds the +`X-Region` header (§3). + +--- + +## 13. Domain: Widgets / dynamic renderer + +Widget manifest is LIVE (static/remote JSON), widget data is derived from products/categories. + +- **`WidgetManifestService`** (`src/app/widgets/registry/widget-manifest.service.ts`) — GETs + `bootstrap.widgetRegistry.manifestUrl` or fallback + `/assets/mock/bootstrap/widget-manifest.json` → `WidgetManifestFile`. +- **`WidgetRegistryService`** (`src/app/widgets/registry/widget-registry.service.ts`), + **`WidgetHostService`** (`src/app/dynamic-renderer/widget-host/widget-host.service.ts`). +- Contracts (`src/app/widgets/contracts/`): `widget-manifest.contract.ts` + (`WidgetManifestEntry/File`, `WidgetSettingsSchema`, `WidgetMetadataSupport`, + `WidgetLayoutSupport`, `WidgetDataSourceName`), `widget-component.contract.ts` + (`WidgetRenderContext`, `RegisteredWidget`, `ResolvedWidget`), `widget-data.contract.ts` + (`HeroWidgetData`, `CategoriesWidgetData`, `ProductCollectionWidgetData`, `BannerWidgetData`, + `HtmlWidgetData`, `PartnersWidgetData`, `FooterWidgetData`, `HeroSlideData`, + `WidgetResolvedContext`). +- Renderer models: `src/app/dynamic-renderer/{page-renderer,section-renderer,widget-host}/*.model.ts`. +- Widget data sources (`featured|latest|category|manual|related|root|parent`) map back onto + the product/category providers of §7–§8. + +--- + +## 14. Admin gateways (feature area) + +`src/app/features/admin/**`. Each domain follows Facade → Gateway (interface) → LocalGateway. +**Only categories and dashboard-metrics use a DI token; all others inject the local class +directly (MOCK-ONLY, no seam).** Only `AdminCategoriesApiGateway` is a real HTTP impl. + +| Domain | Interface | Local (mock) impl | Real impl | DI token | Facade | Seam status | +|---|---|---|---|---|---|---| +| Categories | `admin-categories-gateway.interface.ts` (`AdminCategoriesGateway`) | `admin-categories-local.gateway.ts` | `admin-categories-api.gateway.ts` (**HttpClient**) | `ADMIN_CATEGORIES_GATEWAY` (`admin-categories-gateway.token.ts`) | `AdminCategoriesFacade` | MOCK-SWAPPABLE (real impl exists) | +| Dashboard metrics | `admin-dashboard-metrics.gateway.interface.ts` (`AdminDashboardMetricsGateway`) | `admin-dashboard-metrics.local.gateway.ts` | none | `ADMIN_DASHBOARD_METRICS_GATEWAY` (`admin-dashboard-metrics-gateway.token.ts`) | `AdminDashboardFacade` | MOCK-SWAPPABLE (token only) | +| Orders | `admin-orders-gateway.interface.ts` (`AdminOrdersGateway`) | `admin-orders-local.gateway.ts` | none | **none** | `AdminOrdersFacade` (injects `AdminOrdersLocalGateway`) | MOCK-ONLY (no seam) | +| Products | `admin-products-gateway.interface.ts` (`AdminProductsGateway`) | `admin-products-local.gateway.ts` | none | **none** | `AdminProductsFacade` (injects local) | MOCK-ONLY (no seam) | +| Users | `admin-users-gateway.interface.ts` (`AdminUsersGateway`) | `admin-users-local.gateway.ts` | none | **none** | `AdminUsersFacade` (injects local) | MOCK-ONLY (no seam) | +| Transactions | `admin-transactions-gateway.interface.ts` (`AdminTransactionsGateway`) | `admin-transactions-local.gateway.ts` | none | **none** | `AdminTransactionsFacade` (injects local) | MOCK-ONLY (no seam) | +| Monitoring | `admin-monitoring-gateway.interface.ts` (`AdminMonitoringGateway`) | `admin-monitoring-local.gateway.ts` | none | **none** | `AdminMonitoringFacade` (injects local) | MOCK-ONLY (no seam) | +| Moderation | `admin-moderation-gateway.interface.ts` (`AdminModerationGateway`) | `admin-moderation-local.gateway.ts` | none | **none** | `AdminModerationFacade` (injects local) | MOCK-ONLY (no seam) | +| Customers | (no gateway of its own) | reuses `AdminOrdersLocalGateway` | none | **none** | `AdminCustomersFacade` (injects orders local) | MOCK-ONLY (derived) | +| Analytics | (no gateway of its own) | reuses orders/products/moderation local + `ADMIN_CATEGORIES_GATEWAY` + `AdminDashboardFacade` | none | partial (categories token) | `AdminAnalyticsFacade` | MOCK-ONLY (derived) | + +Gateway interface method contracts (the shapes a backend must satisfy): + +- **`AdminCategoriesGateway`**: `loadCategories(filters)`, `loadCategory(id)`, `createCategory`, + `updateCategory`, `deleteCategory`, `restoreCategory`, `isSlugTaken(slug,excludingId)`. +- **`AdminDashboardMetricsGateway`**: `loadMetrics(): AdminDashboardMetrics`. +- **`AdminOrdersGateway`**: `loadOrders(filters)`, `loadOrder(id)`, `updateStatus(id,status)`, + `requestRefund(id)`, `addNote(id,note,internal)`, `archiveOrder`, `restoreOrder`, `deleteOrder`. +- **`AdminProductsGateway`**: `loadProducts(filters)`, `loadProduct(id)`, `loadCategories()`, + `createProduct`, `updateProduct`, `deleteProduct`, `duplicateProduct`, `archiveProduct`, + `restoreProduct`. +- **`AdminUsersGateway`**: `loadUsers`, `loadRoles`, `loadInvitations`, `loadSessions(userId)`, + `loadAudit(userId)`, `setUserRole`, `setUserStatus`, `inviteUser(email,roleId,scope)`, + `revokeInvitation`, `revokeSession`. +- **`AdminTransactionsGateway`**: `loadTransactions(filters)`, `retryFailed(id)`, + `setFraudFlag(id,flagged)`. +- **`AdminMonitoringGateway`**: `loadEvents(filters)`, `loadQueues()`, `loadWebhooks()`. +- **`AdminModerationGateway`**: `loadReviews(filters)`, `loadReview(id)`, `setReviewStatus`, + `setReviewVisible`, `setReviewPinned`, `setReviewFeatured`, `addModeratorNote`, `deleteReview`, + `loadReports()`, `setReportStatus(id,status)`. + +Admin model files (all under `src/app/features/admin//models/`) — see §23. + +Note: `AdminRole` is defined **twice** with different meaning — `src/app/core/auth/models/ +permission.model.ts` (auth roles `Owner|Administrator|Editor|Support|ReadOnly`) vs +`src/app/features/admin/users/models/admin-user.model.ts` (`AdminRole` interface {id,name,...}). +Flag for backend/naming reconciliation. + +Local gateways are localStorage / in-memory backed (facades also inject `LocalStorageService` +for overlay persistence, e.g. orders/moderation/categories/products). + +--- + +## 15. Domain: Media library + +MOCK-SWAPPABLE via abstract-class token, no real impl. + +- **Contract**: abstract class `MediaRepository` (`src/app/core/media/media-repository.ts`) — + `list(params?)`, `upload(file,options?)`, `remove(id)`, `update(id,patch)`, `listFolders()`. +- **Binding**: `app.config.ts` → `{ provide: MediaRepository, useClass: MockMediaRepository }`. +- **Mock impl**: `MockMediaRepository` (`src/app/core/media/mock-media-repository.service.ts`, + uses `HttpClient` to read seed assets). Also `MediaUsageService` + (`src/app/core/media/media-usage.service.ts`). +- **Facade**: `MediaLibraryFacade` (`src/app/features/backoffice/media/facade/media-library.facade.ts`), + page `media-library-page.component.ts`. +- **Models** (`src/app/core/media/models/media-asset.model.ts`): `MediaAsset`, `MediaAssetKind`, + `MediaSort`, `MediaListParams`, `MediaUploadOptions`, `MediaListResult`. +- Admin-auth interceptor already gates `/media/` paths (§3), anticipating a real media backend. + +--- + +## 16. Domain: Content management / static pages + +**LOCAL-ONLY** — operates on the already-loaded `BootstrapConfig.staticPages`, no dedicated +backend calls. Publishing/writing bootstrap is not implemented client-side (FUTURE). + +- **Facade**: `ContentManagementFacade` (`src/app/features/content-management/facade/content-management.facade.ts`) + → `ContentPageService` (`.../services/content-page.service.ts`). Public API: `pages(bootstrap)`, + `hasSeoContent(page)`, `contentHealth(bootstrap)`, `resolvePage(bootstrap,keyOrSlug,locale)`, + `validatePages(bootstrap)`, `toBootstrapRecord(bootstrap)`, `serializePages(pages)`, + `normalizeSlug`. +- `ContentPageService` maps between bootstrap `StaticPagesConfig` and the editor `ContentPage` + view model (normalize / validate / `toBootstrapRecord`). This is the adapter. +- **Models** (`src/app/features/content-management/models/`): `ContentPage`, + `ContentPageTranslation`, `ContentPageSeoConfig`, `ContentPageStatus`, + `ContentPageBootstrapInput` (`content-page.model.ts`); `LegalPageKey`, `LegalPageDefinition` + (`legal-pages.model.ts`). Backend-shaped counterpart: `StaticPageConfig`, + `StaticPagesConfig`, `ResolvedStaticPage`, `LocalizedHtmlContent`, `LocalizedTextContent` + (`src/app/shared/models/config/static-page.model.ts`). +- Consumers: `static-pages-editor.component.ts`, `page-editor.component.ts`, + `static-page.component.ts` (`src/app/pages/static-page/`), resolved via + `StaticPageResolverService`. + +--- + +## 17. Domain: Project editor / builder + +**LOCAL-ONLY** today — edits an in-memory `BootstrapConfig`, persists drafts to localStorage; +no publish/save-to-backend HTTP call exists. A builder API is declared only as +`BootstrapConfig.apiEndpoints.builder` (runtime-declared, FUTURE). + +- **Facade**: `ProjectEditorFacade` (`src/app/features/project-editor/facade/project-editor.facade.ts`) + — orchestrates undo/redo `History`, injects `ConfigService`, + `ProjectEditorIoService` (JSON import/export of bootstrap), `ProjectEditorPreviewService`, + `LocaleSyncService`, `PlatformRuntimeService`, `ProjectValidator`, + `ProjectEditorDraftStorageService` (localStorage drafts), `EditorSchemaService`. +- Services (`src/app/features/project-editor/services/`): `project-editor-io.service.ts` + (`exportBootstrap`/`importBootstrap` = JSON.stringify/parse), `project-editor-draft-storage.service.ts`, + `project-editor-preview.service.ts`, `project-validator.service.ts`, `locale-sync.service.ts`. + Schema: `schema/editor-schema.service.ts`, `schema/field-schema.model.ts`, `schema/validators/`. +- **Models**: `project-editor.model.ts` (`ProjectEditorState`, `ProjectEditorSectionId`, + `ProjectEditorWidgetPreset`, `BuilderSectionStatus`), `builder/builder-groups.model.ts`. +- Consumers: `project-editor-page.component.ts`, `homepage-section.component.ts`, + `project-editor-nav.component.ts`. Also drives admin products/categories/dashboard facades + (which inject `ProjectEditorFacade`). + +--- + +## 18. Domain: Search + +**LOCAL-ONLY orchestration over the product/category providers** — no dedicated search backend; +`SearchFacade` composes `ProductFacade` + `CategoryFacade` results and manages history/trending/ +autocomplete/cache client-side. + +- **Facade**: `SearchFacade` (`src/app/features/search/facade/search.facade.ts`) injects + `ProductFacade`, `CategoryFacade`, `SearchAutocompleteService`, `SearchHistoryService`, + `SearchTrendingService`, `SearchCacheService`, `SearchStore`, `TranslateService`. +- Services (`src/app/features/search/services/`): `search-autocomplete.service.ts`, + `search-history.service.ts` + `search-history.repository.ts` (interface + `SearchHistoryRepository{load,save,clear}`, localStorage), `search-trending.service.ts`, + `search-cache.service.ts`. Store: `store/search.store.ts`. +- **Models**: `src/app/features/search/models/search.model.ts` (`SearchQuery`, `SearchResult`, + `SearchSuggestion`, `SearchFilterType`, `FilterGroup`, `FilterOption`, `SortOption`, + `SearchHistory`, `SearchAnalyticsEvent`, `SearchNavigationTarget`), `search-state.model.ts` + (`SearchState`). Duplicated under `src/app/core/search/models/`. +- Underlying live traffic is `GET /searchitems` (§4) via `ProductFacade.searchProducts`. + +--- + +## 19. Domain: User experience (wishlist/compare/etc.) + +**LOCAL-ONLY** (guest-first). MOCK-SWAPPABLE token exists for a future authenticated backend. + +- **Interface**: `UserExperienceRepository` + (`src/app/core/user-experience/repositories/user-experience.repository.ts`). +- **DI token**: `USER_EXPERIENCE_REPOSITORY` + (`src/app/core/user-experience/user-experience-repository.token.ts`) → currently always + `LocalUserExperienceRepository` (localStorage). Comment notes it "can be switched to + authenticated repository later." +- **Facade**: `UserExperienceFacade` (`src/app/facades/platform/user-experience.facade.ts`) — + wishlist / compare / recently-viewed / saved-searches / continue-browsing, all signals. +- **Models** (`src/app/core/user-experience/models/user-experience.model.ts`): `FavoriteItem`, + `ComparedProduct`, `RecentlyViewedItem`, `SavedSearch`, `ContinueBrowsingState`. Config shape: + `user-experience-config.model.ts` (limits, from bootstrap). + +--- + +## 20. Domain: Diagnostics + +**LOCAL-ONLY** — inspects runtime/bootstrap/widget state; the one live-ish probe is API ping. + +- **Facade**: `DiagnosticsFacade` (`src/app/features/diagnostics/facade/diagnostics.facade.ts`) + injects `ConfigService`, `TenantResolverService`, `PlatformRuntimeStateService`, + `RuntimeDiagnosticsService`, `WidgetManifestService`, `WidgetRegistryService`, + `RuntimeProviderStrategyService`, `DiagnosticsLoggerService`, `TranslateService`, `Router`. +- Validators: `validators/runtime-diagnostics.validator.ts` (uses `HttpClient` for API health + probe), `bootstrap-diagnostics.validator.ts`, `diagnostics-health-score.util.ts`. +- **Models** (`src/app/features/diagnostics/models/diagnostics.model.ts`): `DiagnosticEntry`, + `DiagnosticSeverity`, `DiagnosticsHealthSummary`, `DiagnosticsReport`. + +--- + +## 21. Facade catalog + +| Facade | File | Depends on | Consumed by (examples) | +|---|---|---|---| +| `ProductFacade` | `facades/platform/product.facade.ts` | `ProductDataService` → `PRODUCT_DATA_PROVIDER` | catalog/product containers, `SearchFacade` | +| `CategoryFacade` | `facades/platform/category.facade.ts` | `CategoryService` → `CATEGORY_REPOSITORY` | catalog nav, `SearchFacade` | +| `SearchFacade` | `features/search/facade/search.facade.ts` | ProductFacade, CategoryFacade, search services | search bar/pages | +| `UserExperienceFacade` | `facades/platform/user-experience.facade.ts` | `USER_EXPERIENCE_REPOSITORY` | wishlist/compare UI | +| `UiRuntimeFacade` | `facades/runtime/ui-runtime.facade.ts` | `ConfigService` | header/branding | +| `WebsiteRuntimeFacade` | `facades/website/website-runtime.facade.ts` | config/page renderer | dynamic pages | +| `AuthFacade` | `core/auth/services/auth-facade.service.ts` | AuthService, SessionService, PermissionService | login/guarded UI | +| `MediaLibraryFacade` | `features/backoffice/media/facade/media-library.facade.ts` | `MediaRepository` | media page | +| `ContentManagementFacade` | `features/content-management/facade/...` | `ContentPageService` (bootstrap) | content dashboard/editor | +| `ProjectEditorFacade` | `features/project-editor/facade/...` | config + editor services (localStorage) | builder pages, admin facades | +| `DiagnosticsFacade` | `features/diagnostics/facade/...` | runtime/config/widget services | diagnostics page | +| `AdminCategoriesFacade` | `features/admin/categories/facade/...` | `ADMIN_CATEGORIES_GATEWAY`, ProjectEditorFacade | admin categories pages | +| `AdminProductsFacade` | `features/admin/products/facade/...` | `AdminProductsLocalGateway`, ProjectEditorFacade | admin products pages | +| `AdminOrdersFacade` | `features/admin/orders/facade/...` | `AdminOrdersLocalGateway` | admin orders pages | +| `AdminUsersFacade` | `features/admin/users/facade/...` | `AdminUsersLocalGateway` | admin users pages | +| `AdminTransactionsFacade` | `features/admin/transactions/facade/...` | `AdminTransactionsLocalGateway` | admin transactions pages | +| `AdminMonitoringFacade` | `features/admin/monitoring/facade/...` | `AdminMonitoringLocalGateway` | admin monitoring page | +| `AdminModerationFacade` | `features/admin/moderation/facade/...` | `AdminModerationLocalGateway` | moderation pages | +| `AdminCustomersFacade` | `features/admin/customers/facade/...` | `AdminOrdersLocalGateway` (derives customers from orders) | customers pages | +| `AdminAnalyticsFacade` | `features/admin/analytics/facade/...` | orders/products/moderation local + `ADMIN_CATEGORIES_GATEWAY` + `AdminDashboardFacade` | analytics page | +| `AdminDashboardFacade` | `features/admin/dashboard/facade/...` | `ADMIN_DASHBOARD_METRICS_GATEWAY`, ProjectEditorFacade, AdminAuthService | admin dashboard | + +`ProductFacade` public API: `getProducts, getProduct, getCategories, searchProducts, +getFeaturedProducts, getLatestProducts, getProductsByCategory, getRelatedProducts, loadRating, +loadReviews, loadQuestions, submitReview, submitQuestion, search(criteria), filter, sort, +loadCatalog`. `CategoryFacade`: signals (`allCategories, categoryTree, rootCategories, +selectedCategory, breadcrumb, children, loading, error`) + `loadCategories, selectCategory, +getAllCategories, getCategoryTree, getRootCategories, getCategoryById, getBreadcrumb, getChildren`. +`UserExperienceFacade`: `isInWishlist, toggleWishlist, clearWishlist, isInCompare, addToCompare, +removeFromCompare, clearCompare, trackRecentlyViewed, saveSearch, removeSavedSearch, +saveContinueBrowsing, getContinueBrowsing` + wishlist/compare signals & counts. + +--- + +## 22. Gateway / provider master table + +| Gateway/provider | Interface path | Mock/local impl | Real/API impl | DI token | Consuming facade(s) | Status | +|---|---|---|---|---|---|---| +| ConfigProvider | `core/config/config-provider.interface.ts` | `core/bootstrap/providers/mock-bootstrap.provider.ts` | `core/bootstrap/providers/api-bootstrap.provider.ts` | `CONFIG_PROVIDER` | UiRuntime, WebsiteRuntime, ProjectEditor, ContentMgmt, Diagnostics (via ConfigService) | LIVE (`GET /bootstrap`) | +| ProductDataProvider | `core/products/providers/product-data-provider.interface.ts` | none bound | `core/products/providers/api-product-data.provider.ts` | `PRODUCT_DATA_PROVIDER` | ProductFacade | LIVE | +| CategoryRepository | `core/categories/repositories/category.repository.ts` | none bound | `core/categories/repositories/api-category.repository.ts` | `CATEGORY_REPOSITORY` | CategoryFacade | LIVE | +| BackofficeDataProvider | `core/backoffice/providers/backoffice-data-provider.interface.ts` | `mock-backoffice-data.provider.ts` | `api-backoffice-data.provider.ts` | `BACKOFFICE_DATA_PROVIDER` | storefront cards | LIVE (`/api/backoffice/*`) | +| UserExperienceRepository | `core/user-experience/repositories/user-experience.repository.ts` | `local-user-experience.repository.ts` | none | `USER_EXPERIENCE_REPOSITORY` | UserExperienceFacade | LOCAL-ONLY | +| MediaRepository | `core/media/media-repository.ts` (abstract class) | `core/media/mock-media-repository.service.ts` | none | `MediaRepository` class (app.config.ts) | MediaLibraryFacade | MOCK-SWAPPABLE | +| SearchHistoryRepository | `features/search/services/search-history.repository.ts` | (localStorage impl) | none | (injected concretely) | SearchFacade (via SearchHistoryService) | LOCAL-ONLY | +| AdminCategoriesGateway | `features/admin/categories/services/admin-categories-gateway.interface.ts` | `admin-categories-local.gateway.ts` | `admin-categories-api.gateway.ts` | `ADMIN_CATEGORIES_GATEWAY` | AdminCategoriesFacade, AdminAnalyticsFacade | MOCK-SWAPPABLE (real impl exists) | +| AdminDashboardMetricsGateway | `features/admin/dashboard/services/admin-dashboard-metrics.gateway.interface.ts` | `admin-dashboard-metrics.local.gateway.ts` | none | `ADMIN_DASHBOARD_METRICS_GATEWAY` | AdminDashboardFacade | MOCK-SWAPPABLE (token only) | +| AdminOrdersGateway | `features/admin/orders/services/admin-orders-gateway.interface.ts` | `admin-orders-local.gateway.ts` | none | **none** | AdminOrdersFacade, AdminCustomersFacade, AdminAnalyticsFacade | MOCK-ONLY (no seam) | +| AdminProductsGateway | `features/admin/products/services/admin-products-gateway.interface.ts` | `admin-products-local.gateway.ts` | none | **none** | AdminProductsFacade, AdminAnalyticsFacade | MOCK-ONLY (no seam) | +| AdminUsersGateway | `features/admin/users/services/admin-users-gateway.interface.ts` | `admin-users-local.gateway.ts` | none | **none** | AdminUsersFacade | MOCK-ONLY (no seam) | +| AdminTransactionsGateway | `features/admin/transactions/services/admin-transactions-gateway.interface.ts` | `admin-transactions-local.gateway.ts` | none | **none** | AdminTransactionsFacade | MOCK-ONLY (no seam) | +| AdminMonitoringGateway | `features/admin/monitoring/services/admin-monitoring-gateway.interface.ts` | `admin-monitoring-local.gateway.ts` | none | **none** | AdminMonitoringFacade | MOCK-ONLY (no seam) | +| AdminModerationGateway | `features/admin/moderation/services/admin-moderation-gateway.interface.ts` | `admin-moderation-local.gateway.ts` | none | **none** | AdminModerationFacade, AdminAnalyticsFacade | MOCK-ONLY (no seam) | +| (Auth session) | — (`TelegramSessionApiService`) | mock via `mockDataInterceptor` | `services/telegram-session-api.service.ts` | n/a (concrete) | AuthService, AdminAuthService, AuthFacade | LIVE | +| (Ed25519 admin auth) | — (`AuthApiService`) | none | `core/auth/services/auth-api.service.ts` | n/a (concrete) | AuthService (Ed25519 flow) | LIVE wiring, backend absent | + +--- + +## 23. Model / DTO catalog + +Grouped by boundary role. B = backend-shaped/wire DTO, V = frontend view model, C = bootstrap +config shape. Adapter column names the mapper if distinct. + +### Core wire DTOs / domain (B) +- `Item` + supporting (`src/app/models/item.model.ts`) — **primary product wire shape**; adapter + `ApiService.normalizeItem()`. +- `Category`, `Subcategory` (`src/app/models/category.model.ts`) — legacy category wire; adapter + `ApiService.normalizeCategory()`. +- `CategoryDto`, `CategoryNameDto` (`src/app/core/categories/dto/category.dto.ts`) — clean-stack + wire DTO; adapter `CategoryMapper`. +- `Region`, `GeoIpResponse` (`src/app/models/location.model.ts`). +- Payment/order DTOs inline in `src/app/services/api.service.ts`: `QrCreateRequest`, + `QrCreateResponse`, `CartPaymentRequest`, `CreateOrderRequest`, `CreateOrderResponse`, + `QrDynamicStatusResponse`. +- Auth: `AuthSession`, `WebSessionStart` (`src/app/models/auth.model.ts`); `AuthChallenge`, + `VerifySignatureRequest`, `AuthTokenPair`, `RefreshTokenRequest`, `JwtClaims` + (`src/app/core/auth/models/auth-api.model.ts`). + +### Domain / view models (V) +- Products: `Product`(=Item alias), `ProductListQuery`, `ProductSearchQuery`, `ProductListResult`, + `ProductFilters`, `RelatedProductsQuery`, `RelatedProductCollection`, `ProductVariantSelection` + (`core/products/models/product-domain.model.ts`). +- Engagement: `Review`, `Answer`, `Question`, `RatingSummary`, `RatingDistributionEntry`, + `EngagementListQuery`, `EngagementListResult`, `SubmitReviewInput`, `SubmitQuestionInput` + (`core/products/models/product-engagement.model.ts`). +- Catalog experience: `SearchCriteria`, `FilterDefinition`, `FilterOption`, `SortDefinition`, + `CatalogView`, `SearchResult` (`core/products/models/catalog-experience.model.ts`); + catalog state (`features/website/catalog/models/catalog-state.model.ts`). +- Category domain: `Category`, `CategoryTranslation` (`core/categories/models/category-domain.model.ts`). +- Media: `MediaAsset` + params/results (`core/media/models/media-asset.model.ts`). +- User experience: `FavoriteItem`, `ComparedProduct`, `RecentlyViewedItem`, `SavedSearch`, + `ContinueBrowsingState` (`core/user-experience/models/user-experience.model.ts`). +- Search: `search.model.ts` + `search-state.model.ts` (`features/search/models/`, dup in `core/search/models/`). +- Content: `ContentPage`, `ContentPageTranslation`, `ContentPageSeoConfig`, `ContentPageStatus`, + `ContentPageBootstrapInput`, `LegalPageKey`, `LegalPageDefinition` + (`features/content-management/models/`); adapter `ContentPageService`. +- Project editor: `ProjectEditorState`, `ProjectEditorSectionId`, `ProjectEditorWidgetPreset`, + `BuilderSectionStatus` (`features/project-editor/models/`), `builder-groups.model.ts`. +- Diagnostics: `DiagnosticEntry`, `DiagnosticsHealthSummary`, `DiagnosticsReport` + (`features/diagnostics/models/diagnostics.model.ts`). +- Widgets: contracts in `src/app/widgets/contracts/*` and renderer `*.model.ts` (see §13). + +### Admin models (V, all under `features/admin//models/`) +- `admin-order.model.ts`: `AdminOrder`, `AdminOrderCustomer`, `AdminOrderPayment`, + `AdminOrderShipping`, `AdminOrderItem`, `AdminOrderTimelineEntry`, `AdminOrderStatus`, + `AdminOrderPaymentStatus`, `AdminOrderTimelineEventKey`, `AdminOrderListFilters`, + `AdminOrdersListResult`. +- `admin-product.model.ts`: `AdminProduct` (+ `AdminProductMedia`, `AdminProductSpecification`, + `AdminProductVariant(Price)`, `AdminProductVariantAttributeDef`, `AdminProductAttribute`, + `AdminProductTranslation`, `AdminProductSeo`, `AdminProductReview`, `AdminProductQuestion`), + `AdminProductListFilters`, `AdminProductsListResult`, `AdminProductCategoryOption`, status/sort/mode types. +- `admin-category.model.ts`: `AdminCategory`, `AdminCategoryTranslation`, `AdminCategorySeo`, + `AdminCategoryAttribute`, `AdminCategoryListFilters`, status/mode types. +- `admin-user.model.ts`: `AdminUser`, `AdminRole`, `AdminInvitation`, `AdminSession`, + `AdminUserAuditEntry`, scope/status/invitation-status types. +- `admin-transaction.model.ts`: `AdminTransaction`, `AdminTransactionAuditEntry`, + `AdminTransactionListFilters`, `AdminTransactionsListResult`, type/status types. +- `admin-monitoring.model.ts`: `AdminMonitoringEvent`, `AdminMonitoringEventFilters`, + `AdminQueue`, `AdminWebhookDelivery`, category/level/queue/webhook status types. +- `admin-review.model.ts`: `AdminReview`, `AdminReviewTimelineEntry`, `AdminReviewListFilters`, + `AdminReviewsListResult`, status/timeline types. +- `admin-report.model.ts`: `AdminReport`, `AdminReportTargetType`, `AdminReportStatus`. +- `admin-customer.model.ts`: `AdminCustomer`. +- `admin-analytics.model.ts`: `AdminAnalyticsSummary`, `AdminAnalyticsSeriesPoint`, + `AdminAnalyticsTopProduct`, `AdminLowStockProduct`, `AdminRecentActivityEntry`, + `AdminMarketplaceHealthCheck`, `AdminProductAnalytics(Row)`, `AdminCustomerAnalytics`, + `AdminRecommendationCard`, date-range/severity/health types. +- `admin-dashboard.model.ts`: `AdminDashboardMetrics`, `AdminDashboardCardState`, + `AdminDashboardQuickAction(Id)`, `AdminDashboardActivityEntry`, `AdminDashboardHealthCheck`, + `AdminDashboardHomeHealthCheck`, `AdminDashboardDraftField`, `AdminDashboardShortcut`, status types. +- Shell: `features/admin/shell/admin-nav.model.ts`. + +### Bootstrap config shapes (C) +All under `src/app/shared/models/config/` — see §6 for the full list (24 files + barrel). + +--- + +## 24. Endpoint URL literals found in code + +Marketplace API (relative to base): `/ping`, `/bootstrap`, `/category`, `/category/{id}`, +`/items/{id}`, `/items/randomitems`, `/searchitems`, `/cart`, `/orders`, `/purchase-email`, +`/regions`, `/websession/{sessionId}`, `/items/{id}/callback`, `/items/{id}/questiion`. + +Backoffice storefront: `/api/backoffice/products`, `/api/backoffice/categories`. + +Payment (`qrApiUrl` = `https://qr.vitanova.network/api`): `/qr`, `/qr/dynamic/{partnerId}/{qrId}`, +`/card/{partnerId}/{orderId}`. Const partner id `web-97ec-9c57-4dde-9037-3a68f7f83750`. + +Session auth (`authApiUrl`): `/users/sessions`, `/users/sessions/{id}`. + +Ed25519 admin auth (`authApiUrl`): `/api/admin/auth/challenge|verify|refresh|logout` +(not implemented server-side). + +Static assets (not backend): `/assets/mock/bootstrap/bootstrap.json`, +`/assets/mock/bootstrap/widget-manifest.json`. + +External (not this platform): `http://ip-api.com/json/...` (geo-IP), +`https://api.qrserver.com/v1/create-qr-code/...` (QR image), `https://t.me/{bot}`, +`tg://resolve?...`. + +`mockDataInterceptor` URL matchers (mock mode only): `/ping`, `/users/sessions[/{id}]`, +`/category`, `/category/{id}`, `/items/{id}`, `/searchitems`, `/randomitems`, `/cart`, +`/websession/{id}[/qr]`, `/qr`, `/items/{id}/callback`, `/purchase-email`, `/qr/payment/{id}`. + +**No literal `/admin/*`, `/builder/*`, or per-admin-domain backoffice CRUD paths exist in code.** +Those live only as `apiEndpoints.{builder,backoffice}` records inside the runtime bootstrap +document, and admin gateways are in-memory (they never construct a URL). Any concrete admin CRUD +path is therefore a proposal, not a verified literal — consistent with `docs/BACKEND_API.md` +Assumption #2. + +The admin-auth-headers interceptor gates these path **segments** (anticipatory, not called yet): +`/admin/`, `/backoffice/`, `/builder/`, `/media/`. + +--- + +## 25. Cross-check against existing docs + +Skimmed: `docs/BACKEND_API.md` (canonical master spec, CURRENT/PLANNED/FUTURE tagging), +`docs/AUTH.md`, `docs/ADMIN.md`, `docs/BACKEND_API_REMAINING_WORK.md`, +`docs/architecture/foundation/**`, `docs/backend/BACKEND-INTEGRATION.md`. + +Agreements (preserve these conventions downstream): +- `docs/BACKEND_API.md` already uses `GET /bootstrap`, the `*LocalGateway` → `*ApiGateway` + rebind pattern, and frozen auth/payment (ADR-010). Its CURRENT/PLANNED/FUTURE tagging maps + cleanly onto LIVE / MOCK-SWAPPABLE / MOCK-ONLY here. +- Assumption #2 (builder/backoffice paths are proposals, not literals) is confirmed by code. +- `submitQuestion` typo `questiion` and `callback` review path confirmed against code. + +Discrepancies / things to flag for a human: +1. **`docs/BACKEND_API.md` PLANNED framing implies every admin domain is a token rebind.** + In code, only `ADMIN_CATEGORIES_GATEWAY` and `ADMIN_DASHBOARD_METRICS_GATEWAY` are + token-bound. Orders, products, users, transactions, monitoring, moderation (and derived + customers/analytics) inject the concrete `*LocalGateway` directly — no seam. A backend + integration for those requires adding a token first. This should be reconciled in the docs. +2. **Only one real admin API impl exists** (`AdminCategoriesApiGateway`). Everything else admin + is mock. Docs that describe admin endpoints as "PLANNED, served by local gateway" are + accurate in spirit but the swap ergonomics differ per domain (see #1). +3. **Duplicate `Category` types** (`src/app/models/category.model.ts` vs + `core/categories/models/category-domain.model.ts`) and **duplicate `AdminRole`** + (auth `permission.model.ts` string-union vs users `admin-user.model.ts` interface) — naming + collisions a backend/contract author should be warned about. +4. **Duplicate search models** under `features/search/models/` and `core/search/models/`. +5. **Content-management & project-editor "save/publish" has no client HTTP call.** Docs that + imply a builder publish endpoint should tag it FUTURE — there is no `PUT /bootstrap` or + builder-write call anywhere in code today; changes live in localStorage drafts + in-memory + bootstrap only. +6. `PRODUCT_DATA_PROVIDER` / `CATEGORY_REPOSITORY` token factories return the Api provider even + in `mock` mode (no mock class bound) — so `useMockData` does NOT mock products/categories at + the provider layer; mocking there relies entirely on `mockDataInterceptor`. Worth noting if a + doc claims a mock product provider exists. + +--- + +_Generated from source on branch `B2B`. Every path above is repo-relative to +`F:\dx\remote\marketplaces\`._