Files
marketplaces/docs/BACKEND.md

5210 lines
282 KiB
Markdown
Raw Normal View History

docs: final documentation consolidation - one canonical doc set Audited every *.md in docs/ and root. Merged five overlapping backend docs (BACKEND_INTEGRATION.md + AUTHENTICATION.md + ERROR_CONTRACT.md + MAINTENANCE_MODE.md + the already-archived BACKEND_API.md/ BACKEND_API_REMAINING_WORK.md) into one canonical docs/BACKEND.md (4775 lines, 10 numbered sections) - deleted the four standalone files outright now that their content is fully inlined. Archived (not deleted - real historical value): ADMIN.md (Sprint 19-28 build log, sprint-report-shaped, not a living reference) and FRONTEND-ROADMAP.md (despite its name, a shipped-history changelog with detail no other doc has - not a forward roadmap, so keeping it in root alongside NEXT_PHASE.md was exactly the "10 roadmaps" confusion being cleaned up). Deleted outright (zero value): SPRINTS.md - a leftover copy-pasted sprint-kickoff prompt saved as a file, not documentation. Rewrote docs/PROJECT_STATUS.md with completion-percentage estimates per area (frontend/backend/UI/admin/storefront) and an explicit first-customer-readiness call. Rewrote docs/NEXT_PHASE.md to the strict 5-phase structure (backend integration -> production testing -> performance -> monitoring -> v2 ideas), pointing to PRODUCT_BACKLOG .md/FUTURE_FEATURES.md for phase 5 detail instead of duplicating it. Rewrote root README.md - was stale (referenced deleted pages/info, pages/legal folders from a prior RC pass), now covers architecture, frontend/backend status, how to run, mock<->API switch mechanism (useMockData in environment.ts), current folder structure, and a documentation map. Updated docs/PROJECT_INDEX.md (the stated entry point) to link only the surviving doc set - every remaining document is reachable from it. Fixed every broken/stale cross-reference to the deleted/renamed backend docs across ARCHITECTURE.md, EDITOR.md, FRONTEND.md, PROJECT-STRUCTURE.md, StaticPages.md, KNOWN-ISSUES.md (10 individual link fixes, verified by repo-wide grep before and after). Left CHANGELOG.md's two historical entries untouched - changelogs are append-only history, not live navigation, editing past entries would misrepresent what was true at the time. Not touched (explicitly out of scope): docs/architecture/foundation/** (enforced ADRs/governance, permanent not sprint-shaped), docs/context/** (Barry Cache infrastructure, "do not edit by hand" per CLAUDE.md), .claude/worktrees/** (separate git worktrees containing an unrelated project's docs, not this repo's documentation). docs/ root: 22 files -> 16. Plus 5 in docs/archive/ (was 3).
2026-07-26 14:56:25 +04:00
# Backend — Canonical Specification
docs: finalize BACKEND.md as a self-contained implementation prompt Goal: this document alone, pasted into a fresh Claude session, should be enough for a backend dev to implement against without getting stuck or inventing conventions the frontend doesn't actually need. Fixed a real bug: ~65 cross-references throughout the document pointed to docs/AUTHENTICATION.md, docs/ERROR_CONTRACT.md, docs/MAINTENANCE_MODE.md - three sibling docs that were deleted and fully merged into this document's own §4/§6/§10 during the earlier doc-consolidation pass, but the in-text references were never updated. A fresh agent following those links would hit dead ends repeatedly. Bulk-replaced with in- document section references; hand-fixed ~4 sentences that framed §6/ §10 as "sibling task, in progress" (stale - both are complete, this document's §6/§10 already are the settled contract, nothing to wait on). Added "Recommended default" to every item across the document's 5 consolidated "Requires backend decision" registers (§2.12 framework, §3.21 CRUD cross-cutting - 18 items, §4 §12 auth open items - 9 items, §6 error-model summary - 9 items, §10 maintenance-mode list - 5 items). Each default is derived from what the frontend already implies or standard REST/security convention - no invented APIs or business rules. ~6 items are explicitly flagged as real business/security decisions instead (order state-machine rules, Ed25519 cutover strategy, refresh-token reuse-detection posture) since those carry consequences no amount of frontend-code-reading can resolve. Added a "How to use this document" preamble up front: work in §9's dependency order not document order, apply recommended defaults and keep moving, only stop for the explicitly-flagged business/security items, don't invent beyond what's written or directly implied. Frontend side: confirmed nothing else is left. docs/TODO.md already has zero blockers; docs/KNOWN-ISSUES.md's one open item (unreachable Ed25519 error-code UI) is correctly left open and documented rather than faked closed - fixing it needs a real backend emitting real distinguishable error codes, which doesn't exist yet and can't be fabricated without inventing an API contract. No frontend code touched. No architecture changed. No APIs invented.
2026-07-26 16:10:33 +04:00
**This is the ONE document a backend engineer needs.** It is the single source of truth for backend implementation — architecture, bootstrap, authentication, JWT, Ed25519 public-key login, permissions, maintenance mode, error contract, every endpoint, DTOs, request/response schemas, uploads, pagination, filters, sorting, publish workflow, media, builder, examples, and a top-to-bottom implementation checklist. Authentication (§4), the Error Model (§6), and Maintenance Mode (§10) are sections of this same document, not separate files — earlier drafts existed as standalone `AUTHENTICATION.md`/`ERROR_CONTRACT.md`/`MAINTENANCE_MODE.md` files and were fully merged in; those files no longer exist. This document also supersedes (already archived, content re-derived from current source) `docs/archive/BACKEND_API.md` and `docs/archive/BACKEND_API_REMAINING_WORK.md`.
Everything here is derived from the actual current frontend source code (branch `B2B`), not from prior/stale documentation. Primary input: `docs/context/BACKEND-AUDIT.md` (exhaustive audit of every HTTP call, gateway, facade, and model in the frontend).
**Convention used throughout:** where the frontend already implies a concrete behavior, it's documented as-is. Where the frontend has no opinion and a real backend needs one, it's marked **"Requires backend decision"** — nothing beyond what the frontend requires is invented.
docs: finalize BACKEND.md as a self-contained implementation prompt Goal: this document alone, pasted into a fresh Claude session, should be enough for a backend dev to implement against without getting stuck or inventing conventions the frontend doesn't actually need. Fixed a real bug: ~65 cross-references throughout the document pointed to docs/AUTHENTICATION.md, docs/ERROR_CONTRACT.md, docs/MAINTENANCE_MODE.md - three sibling docs that were deleted and fully merged into this document's own §4/§6/§10 during the earlier doc-consolidation pass, but the in-text references were never updated. A fresh agent following those links would hit dead ends repeatedly. Bulk-replaced with in- document section references; hand-fixed ~4 sentences that framed §6/ §10 as "sibling task, in progress" (stale - both are complete, this document's §6/§10 already are the settled contract, nothing to wait on). Added "Recommended default" to every item across the document's 5 consolidated "Requires backend decision" registers (§2.12 framework, §3.21 CRUD cross-cutting - 18 items, §4 §12 auth open items - 9 items, §6 error-model summary - 9 items, §10 maintenance-mode list - 5 items). Each default is derived from what the frontend already implies or standard REST/security convention - no invented APIs or business rules. ~6 items are explicitly flagged as real business/security decisions instead (order state-machine rules, Ed25519 cutover strategy, refresh-token reuse-detection posture) since those carry consequences no amount of frontend-code-reading can resolve. Added a "How to use this document" preamble up front: work in §9's dependency order not document order, apply recommended defaults and keep moving, only stop for the explicitly-flagged business/security items, don't invent beyond what's written or directly implied. Frontend side: confirmed nothing else is left. docs/TODO.md already has zero blockers; docs/KNOWN-ISSUES.md's one open item (unreachable Ed25519 error-code UI) is correctly left open and documented rather than faked closed - fixing it needs a real backend emitting real distinguishable error codes, which doesn't exist yet and can't be fabricated without inventing an API contract. No frontend code touched. No architecture changed. No APIs invented.
2026-07-26 16:10:33 +04:00
## How to use this document (read this first if you're implementing from it)
You are building a real backend for an existing, feature-complete Angular
frontend (branch `B2B`) that currently runs entirely on mock/local data. This
document is the complete contract — every endpoint, DTO, status code, and
convention the frontend already expects. Treat it as your spec, not as
background reading.
1. **Work in dependency order**, not top-to-bottom document order: §9 (Backend
Checklist) gives the actual build sequence in 6 phases (foundation → auth/
tenant/bootstrap → read-heavy catalog → write-heavy customer domains →
admin domains → builder/CMS → hardening). Start there.
2. **Every "Requires backend decision" marker has a Recommended default**
attached to it (in §2.12, §3.21, §4 §12, §6's summary, and §10's list —
the five per-section consolidated registers). Apply the recommended
default and keep moving. Do not stop and wait for human input on these —
they exist so you don't have to invent a convention from nothing, and
they were chosen to match what the frontend already assumes.
3. **A small number of items are explicitly marked as business/security
decisions, not technical ones** (e.g. order status-transition rules, the
Ed25519 cutover strategy, refresh-token reuse detection posture). These
are the only points where you should actually stop and ask a human,
because they carry money, legal, or security consequences the frontend
code can't tell you the answer to.
4. **Do not invent new endpoints, fields, or business rules beyond what's
written here or directly implied by an existing frontend call.** If you
find yourself needing something this document doesn't cover and no
recommended default applies, that's a real gap — flag it explicitly
rather than guessing silently.
5. **§8 (Real Backend Implementation Guide)** has the frontend-side
mechanics: which DI-token gateways to swap, which facades change, worked
DTO-mapping examples. Read it before touching any single domain's
endpoints in §3 — it explains the pattern every domain repeats.
6. **Verify against the live frontend where you can.** Every literal
(non-PROPOSED) path, DTO, and example in this document was checked
against actual source on `B2B` — but the frontend evolves. If your
implementation and this document ever disagree with what the running
frontend actually sends/expects, the frontend's actual behavior wins;
flag the doc as stale rather than building against a description that
no longer matches reality.
## Table of contents
1. [Bootstrap](#1-bootstrap)
2. [Endpoint Documentation Framework](#2-endpoint-documentation-framework)
3. [CRUD Contracts](#3-crud-contracts)
4. [Authentication](#4-authentication)
5. [Security](#5-security)
6. [Error Model](#6-error-model)
7. [Uploads](#7-uploads)
8. [Real Backend Implementation Guide](#8-real-backend-implementation-guide)
9. [Backend Checklist](#9-backend-checklist)
docs: final documentation consolidation - one canonical doc set Audited every *.md in docs/ and root. Merged five overlapping backend docs (BACKEND_INTEGRATION.md + AUTHENTICATION.md + ERROR_CONTRACT.md + MAINTENANCE_MODE.md + the already-archived BACKEND_API.md/ BACKEND_API_REMAINING_WORK.md) into one canonical docs/BACKEND.md (4775 lines, 10 numbered sections) - deleted the four standalone files outright now that their content is fully inlined. Archived (not deleted - real historical value): ADMIN.md (Sprint 19-28 build log, sprint-report-shaped, not a living reference) and FRONTEND-ROADMAP.md (despite its name, a shipped-history changelog with detail no other doc has - not a forward roadmap, so keeping it in root alongside NEXT_PHASE.md was exactly the "10 roadmaps" confusion being cleaned up). Deleted outright (zero value): SPRINTS.md - a leftover copy-pasted sprint-kickoff prompt saved as a file, not documentation. Rewrote docs/PROJECT_STATUS.md with completion-percentage estimates per area (frontend/backend/UI/admin/storefront) and an explicit first-customer-readiness call. Rewrote docs/NEXT_PHASE.md to the strict 5-phase structure (backend integration -> production testing -> performance -> monitoring -> v2 ideas), pointing to PRODUCT_BACKLOG .md/FUTURE_FEATURES.md for phase 5 detail instead of duplicating it. Rewrote root README.md - was stale (referenced deleted pages/info, pages/legal folders from a prior RC pass), now covers architecture, frontend/backend status, how to run, mock<->API switch mechanism (useMockData in environment.ts), current folder structure, and a documentation map. Updated docs/PROJECT_INDEX.md (the stated entry point) to link only the surviving doc set - every remaining document is reachable from it. Fixed every broken/stale cross-reference to the deleted/renamed backend docs across ARCHITECTURE.md, EDITOR.md, FRONTEND.md, PROJECT-STRUCTURE.md, StaticPages.md, KNOWN-ISSUES.md (10 individual link fixes, verified by repo-wide grep before and after). Left CHANGELOG.md's two historical entries untouched - changelogs are append-only history, not live navigation, editing past entries would misrepresent what was true at the time. Not touched (explicitly out of scope): docs/architecture/foundation/** (enforced ADRs/governance, permanent not sprint-shaped), docs/context/** (Barry Cache infrastructure, "do not edit by hand" per CLAUDE.md), .claude/worktrees/** (separate git worktrees containing an unrelated project's docs, not this repo's documentation). docs/ root: 22 files -> 16. Plus 5 in docs/archive/ (was 3).
2026-07-26 14:56:25 +04:00
10. [Maintenance Mode](#10-maintenance-mode)
docs(backend): add Seller Management section to BACKEND.md docs/BACKEND_API.md no longer exists as a live file (merged into BACKEND.md in an earlier consolidation pass, per that doc's own intro - only docs/archive/BACKEND_API.md remains, historical only). This mission's "update BACKEND_API.md" instruction is fulfilled by extending the doc that actually supersedes it: new §11 "Seller Management (Optional Capability)", added to the top-of-file table of contents, no existing section renumbered or altered. Every subsection explicitly tagged Implemented / Planned / Future, matching the same legend used in docs/architecture/foundation/ Seller-Management.md (the frontend-side capability doc this section is the backend counterpart to): - 11.2 Future entities: Marketplace (Implemented, existing TenantConfig unchanged), Seller/SellerBranding (Planned - frontend types exist, no backend schema), SellerUser/SellerSettings/ SellerInvitation (Future - no type, no concept, named for roadmap completeness only). - 11.3 Future endpoints: Seller CRUD/Activation/Invitations/Branding/ Analytics/Dashboard - all Future, none designed, each noted as following the existing mock-to-API-gateway pattern (SS8) once built. - 11.4 Authentication: SellerPermissionRole (4 roles) explicitly flagged as a separate vocabulary from the existing, live AdminRole - not merged, no guard wired, zero auth change. - 11.5 Domain resolution: market.com -> Marketplace is Implemented today (ADR-001, backend-Host-resolved); nike.market.com -> Marketplace -> Seller is Future, no backend resolves it - and per the storefront audit, needs no frontend routing change once it does. - 11.6 Bootstrap additions: modules/modules.sellerManagement documented as Implemented-as-contract (typed, always false/absent today); sellerScope/sellerBranding as Planned with an explicitly flagged open question (SellerConfig vs SellerBranding nesting not reconciled); permissions noted as existing/unrelated today. - 11.7 Checkout modes: Unified Order vs Split Orders - Future, not designed, flagged as the single most consequential undecided item for backend design given payments/refunds/reporting all depend on it. - 11.8 Product ownership: sellerId? on Item/AdminProduct/AdminOrder - Implemented as schema only (optional, absent = marketplace-owned, verified backward-compatible via tsc staying clean). Existing products remain valid with no migration required - NULL/absent ownership documented as a permanent state, not transitional. No backend implemented. No frontend code touched. Documentation only.
2026-07-26 22:41:57 +04:00
11. [Seller Management (Optional Capability)](#11-seller-management-optional-capability)
---
## 1. Bootstrap
The bootstrap document is the single runtime-configuration payload that drives
the entire multi-tenant storefront/builder/backoffice. It is fetched once at
app startup and held in memory; nearly every feature (theme, navigation,
localization, catalog behavior, static pages, feature flags, widget registry)
reads from it rather than from dedicated per-feature endpoints.
Source of truth for this section:
`src/app/core/bootstrap/providers/api-bootstrap.provider.ts`,
`src/app/core/config/config.service.ts`,
`src/app/core/config/api-config.service.ts`,
`src/app/core/config/tenant-resolver.service.ts`,
`src/app/shared/models/config/*`, and the mock document
`src/assets/mock/bootstrap/bootstrap.json`. Cross-referenced against
docs: final project closeout - classify TODO, backend spec, status Classified every TODO.md item into one of DONE/BACKEND/PRODUCT DECISION/FUTURE VERSION/BUG, verified against source, not against prior docs: - BACKEND items (bootstrap content, builder draft/publish, 6 admin CRUD domains, media pipeline) confirmed already covered by BACKEND_INTEGRATION.md; appended a mapping appendix rather than duplicating raw bullets. Fixed 22 stale internal BACKEND_API.md cross-references left over from before that file was archived. - PRODUCT DECISION items (dark mode, brand-color WCAG contrast, stars.component token gap, footer Contacts content, advanced analytics, payment providers) moved to new docs/PRODUCT_BACKLOG.md. - FUTURE VERSION items (Angular 22, bundle splitting, cart-modal composition cleanup, hero-spacing investigation) moved to new docs/FUTURE_FEATURES.md. - BUG: rewrote docs/KNOWN-ISSUES.md down to the one real, verified, currently-reproducible frontend bug (Ed25519 admin-auth error codes session-expired/invalid-signature are unreachable - toAuthErrorShape() never reads a body error code, only maps HTTP status, and no status ever produces those two codes - confirmed by reading auth.service.ts + auth-error.model.ts). Condensed the "Fixed" history instead of carrying full verbose repro text forward. - DONE items removed outright (dead-code deletion, dashboard false positive, RC-02 fixes, stale "dynamic-renderer unwired"/"178 missing keys" claims already disproven by source). docs/TODO.md rewritten to the exact "no blockers" template - nothing left qualifies as a release blocker. New docs/PROJECT_STATUS.md: honest per-area status (frontend/backend/ docs/auth/builder/storefront/admin), known limitations, and explicit production/backend/demo readiness calls - including correcting an initial draft's unpushed-commit count (53, not 10, per git log origin/B2B..HEAD). New docs/NEXT_PHASE.md: work that can only start once a real backend exists (gateway swap-in, mock removal, dormant-auth activation, role enforcement, integration/E2E tests, perf profiling, monitoring, maintenance-mode UI). docs/PROJECT_INDEX.md (the stated entry point) updated to link the new doc set and stop pointing at the now-archived BACKEND_API.md/AUTH.md. docs/FRONTEND-ROADMAP.md's "Known open items" replaced with pointers to the new category-split docs instead of a duplicated mixed list. Not swept: a handful of low-traffic docs (architecture ADRs, FRONTEND.md, EDITOR.md, ARCHITECTURE.md, PROJECT-STRUCTURE.md, StaticPages.md, ADMIN.md) still reference the old BACKEND_API.md/ AUTH.md filenames - noted as a known gap in PROJECT_STATUS.md rather than touched blindly, since they're historical-context docs, not the navigation entry point.
2026-07-26 12:35:26 +04:00
`docs/context/BACKEND-AUDIT.md` §6, §2 and the prior `docs/archive/BACKEND_API.md`.
### 1.1 Request contract
| Property | Value |
|---|---|
| Method | `GET` |
| Route (API mode) | `/bootstrap` — relative; rewritten onto the resolved tenant API base by `apiBaseUrlInterceptor` (see §1.10) |
| Route (mock/local mode) | `GET /assets/mock/bootstrap/bootstrap.json` (static asset, no backend) |
| Request body | none |
| Query params | none |
| Auth requirement | **None.** Bootstrap is fetched before any login and must be publicly cacheable per tenant. It carries no `Authorization` header and no `WebSessionID` is required for it to succeed. |
**Headers.** The provider itself (`ApiBootstrapProvider`) sets no explicit
headers — it issues a plain `HttpClient.get<BootstrapConfig>('/bootstrap')`.
Because the URL is `/api`-relative only after interceptor rewrite (the literal
is `/bootstrap`, not `/api/bootstrap`), whether `apiHeadersInterceptor`'s
marketplace headers (`X-Region`, `X-Language`, `Currency`, `WebSessionID`)
attach depends on `ApiConfigService.isApiRequest()` matching the resolved URL.
In practice the backend must treat all of these as **optional** on the
bootstrap call — none are load-bearing for it, and the document is expected to
resolve from tenant/origin alone (§1.9).
> **Requires backend decision:** whether the bootstrap endpoint should honor
> an `X-Language`/`Accept-Language` hint to pre-select a localization, or
> always return the full multi-locale document (the frontend today always
> receives and holds the full multi-locale document and selects locale
> client-side).
### 1.2 Which provider actually fires
`CONFIG_PROVIDER` (`src/app/core/config/config-provider.token.ts`) is a factory
bound by `RuntimeProviderStrategyService.getBootstrapProviderMode()`:
- Returns **mock** (`MockBootstrapProvider` → static asset) when
`environment.useMockData === true`, **or** when
`environment.useMockBootstrapOnLocal === true` **and** the host is localhost.
- Otherwise returns **api** (`ApiBootstrapProvider``GET /bootstrap`).
With the current `environment.ts` (`useMockData: false`,
`useMockBootstrapOnLocal: true`), a localhost dev session reads the static
`bootstrap.json`; a deployed tenant host reads the live `GET /bootstrap`.
### 1.3 Response shape — `BootstrapConfig`
Copied faithfully from
`src/app/shared/models/config/bootstrap-config.model.ts`:
```ts
export interface BootstrapConfig {
schemaVersion: string;
generatedAt: string;
tenant: TenantConfig;
branding: BrandingConfig;
theme: ThemeConfig;
company: CompanyConfig;
featureFlags: FeatureFlagsConfig;
features?: MarketplaceFeaturesConfig;
apiEndpoints: ApiEndpointsConfig;
localization: LocalizationConfig;
seo: SeoConfig;
permissions: PermissionsConfig;
header?: HeaderConfig;
catalog?: CatalogConfig;
layout?: PlatformLayoutConfig;
navigation: NavigationConfig;
footer?: FooterConfig;
productPage?: ProductPageConfig;
userExperience?: UserExperienceConfig;
pages: PageConfig[];
staticPages?: StaticPagesConfig;
widgetRegistry?: WidgetRegistryConfig;
}
```
Required (non-optional) top-level keys the backend must always emit:
`schemaVersion, generatedAt, tenant, branding, theme, company, featureFlags,
apiEndpoints, localization, seo, permissions, navigation, pages`. Everything
marked `?` may be omitted; the frontend supplies defaults (e.g.
`DEFAULT_CATALOG_CONFIG`, `DEFAULT_USER_EXPERIENCE_CONFIG`) when absent.
Each nested field, with its source model file under
`src/app/shared/models/config/`:
- **`schemaVersion`** (`string`) — document schema version (e.g. `"1.0.0"`).
Not enforced by a validator today; see §1.6.
- **`generatedAt`** (`string`, ISO 8601) — when the tenant's config snapshot
was produced. Advisory/diagnostic only today.
- **`tenant`** (`TenantConfig`, `tenant.model.ts`) —
`{ id (UUID), slug, code, host, name, websiteBaseUrl, builderBaseUrl,
backofficeBaseUrl, defaultLocale, supportedLocales[], defaultCurrency,
supportedCurrencies[], timezone }`. Identifies the tenant and its per-surface
base URLs, locale/currency sets, and timezone.
- **`branding`** (`BrandingConfig`, `branding.model.ts`) —
`{ brandName, legalName, slogan?, logoUrl, logoCompactUrl?, faviconUrl,
appIconUrl?, socialImageUrl?, galleryUrls?, supportEmail?, supportPhone? }`.
Drives header logo, favicon, PWA icon, social share image.
- **`theme`** (`ThemeConfig`, `theme.model.ts`) —
`{ themeId, mode: 'light'|'dark'|'system', palette (12 named colors:
primary/secondary/accent/success/warning/danger/info/textPrimary/
textSecondary/backgroundPrimary/backgroundSecondary/border), typography
(primaryFontFamily, headingFontFamily?, baseFontSize), spacing (unit, scale[]),
borderRadiusScale (Record<string,string>), shadows (Record<string,string>),
iconSet }`. Consumed by `ThemeEngineService`/`BrandingEngineService` to emit
CSS variables at runtime.
- **`company`** (`CompanyConfig`) — legal entity + address + contacts block
(see mock JSON §1.5 for shape: `companyName, registrationNumber, taxId,
address{country,region,city,street,postalCode}, contacts{email,phone,
telegram,website}`).
- **`featureFlags`** (`FeatureFlagsConfig`, `feature-flags.model.ts`) — a
`Record<string, boolean>` with well-known keys `wishlist, compare, reviews,
questions, comments, recommendations, blog, chat, analytics, notifications,
coupons, loyalty, giftCards, invoices` plus an open index signature for
arbitrary custom flags. Toggle features on/off per tenant.
- **`features`** (`MarketplaceFeaturesConfig`, optional) — richer per-feature
config beyond boolean flags.
- **`apiEndpoints`** (`ApiEndpointsConfig`, `api-endpoints.model.ts`) —
`{ bootstrap: ApiEndpointConfig, website: Record<string,ApiEndpointConfig>,
builder: Record<string,ApiEndpointConfig>, backoffice:
Record<string,ApiEndpointConfig> }` where `ApiEndpointConfig =
{ path, method, timeoutMs? }`. **This is where a tenant declares its planned
per-surface endpoint paths at runtime.** The `website/builder/backoffice`
maps are empty `{}` in the mock today — no builder/backoffice CRUD path
exists as a code literal anywhere (BACKEND-AUDIT §24); those endpoints, if
ever built, are declared here rather than hard-coded.
- **`localization`** (`LocalizationConfig`, `localization.model.ts`) —
`{ defaultLocale, supportedLocales[], currencyByLocale (Record<locale,
currency>), dictionaries: [{ locale, dictionaryUrl, version }] }`. Note the
three supported locales are `ru, en, hy`; the marketplace API uses codes
`RU, EN, AM` (mapped by `apiHeadersInterceptor` / `ApiService.normalizeLang`,
`am↔hy`).
- **`seo`** (`SeoConfig`, `seo.model.ts`) —
`{ default: SeoPageConfig, byPageKey: Record<pageKey, SeoPageConfig> }` where
`SeoPageConfig = { title, description, canonicalUrl?, robots?, metaTags? }`.
- **`permissions`** (`PermissionsConfig`, `permissions.model.ts`) —
`{ definitions: [{ key, description? }], roles: [{ role, permissions[] }] }`.
Bootstrap-level RBAC catalog. (Distinct from the Ed25519 JWT `AdminRole`
docs: finalize BACKEND.md as a self-contained implementation prompt Goal: this document alone, pasted into a fresh Claude session, should be enough for a backend dev to implement against without getting stuck or inventing conventions the frontend doesn't actually need. Fixed a real bug: ~65 cross-references throughout the document pointed to docs/AUTHENTICATION.md, docs/ERROR_CONTRACT.md, docs/MAINTENANCE_MODE.md - three sibling docs that were deleted and fully merged into this document's own §4/§6/§10 during the earlier doc-consolidation pass, but the in-text references were never updated. A fresh agent following those links would hit dead ends repeatedly. Bulk-replaced with in- document section references; hand-fixed ~4 sentences that framed §6/ §10 as "sibling task, in progress" (stale - both are complete, this document's §6/§10 already are the settled contract, nothing to wait on). Added "Recommended default" to every item across the document's 5 consolidated "Requires backend decision" registers (§2.12 framework, §3.21 CRUD cross-cutting - 18 items, §4 §12 auth open items - 9 items, §6 error-model summary - 9 items, §10 maintenance-mode list - 5 items). Each default is derived from what the frontend already implies or standard REST/security convention - no invented APIs or business rules. ~6 items are explicitly flagged as real business/security decisions instead (order state-machine rules, Ed25519 cutover strategy, refresh-token reuse-detection posture) since those carry consequences no amount of frontend-code-reading can resolve. Added a "How to use this document" preamble up front: work in §9's dependency order not document order, apply recommended defaults and keep moving, only stop for the explicitly-flagged business/security items, don't invent beyond what's written or directly implied. Frontend side: confirmed nothing else is left. docs/TODO.md already has zero blockers; docs/KNOWN-ISSUES.md's one open item (unreachable Ed25519 error-code UI) is correctly left open and documented rather than faked closed - fixing it needs a real backend emitting real distinguishable error codes, which doesn't exist yet and can't be fabricated without inventing an API contract. No frontend code touched. No architecture changed. No APIs invented.
2026-07-26 16:10:33 +04:00
union — see `§4 (Authentication, this document)` §9 for the flagged `AdminRole` naming
collision.)
- **`header`** (`HeaderConfig`, optional) — header layout config.
- **`catalog`** (`CatalogConfig`, `catalog-config.model.ts`, optional) — the
storefront catalog behavior contract:
`{ layout, loadingStrategy: 'pagination'|'loadMore'|'infiniteScroll',
navigationMode, defaultSort, availableSorts[], enabledFilters[],
showBreadcrumbs, showCategoryBanner, showSubcategoryChips, showRatings,
showDiscounts, showAvailability, suggestionsEnabled, searchHistoryEnabled }`.
`defaultSort`/`availableSorts` enumerate `relevance | latest | price_asc |
price_desc | rating | popular | discount` (see §2.3). Defaults in
`DEFAULT_CATALOG_CONFIG`.
- **`layout`** (`PlatformLayoutConfig`, optional) — global layout type.
- **`navigation`** (`NavigationConfig`, `navigation.model.ts`) —
`{ header: NavigationItemConfig[], footer: NavigationItemConfig[] |
FooterNavigationGroupConfig[], sidebar?: NavigationItemConfig[] }`. Items
carry `{ id, labelKey?|label?, route?, icon?, order?, visible?,
visibleWhenFlags?, children? }` — note `visibleWhenFlags` lets a nav item be
gated on a `featureFlags` key.
- **`footer`** (`FooterConfig`, `footer-config.model.ts`, optional) —
`{ logoUrl?, paymentIcons?, copyrightText? (string|localized), columns?
(FooterColumnConfig[]), socialLinks? }`. `legalPageKeys`/`staticPageKeys` are
`@deprecated` in favor of `columns` but still resolve for older saved configs.
- **`productPage`** (`ProductPageConfig`, optional) — per-PDP config; in the
mock: `rating{enabled}, reviews{enabled,pageSize,showSummary},
questions{enabled,pageSize}, tabs{enabled,items[]}, relatedProducts{enabled}`.
Note `reviews.pageSize`/`questions.pageSize` (5 in mock) are the storefront
engagement page sizes (§2.2).
- **`userExperience`** (`UserExperienceConfig`, `user-experience-config.model.ts`,
optional) — `{ wishlist, compare{maxItems,...}, recentlyViewed{maxItems,...},
share, continueBrowsing, savedSearches{maxItems} }` limits/toggles for the
guest-first UX features. Defaults in `DEFAULT_USER_EXPERIENCE_CONFIG`.
- **`pages`** (`PageConfig[]`, `page.model.ts`) — the dynamic page tree the
renderer builds routes from: each `{ id, key, title, route{path,exact?},
layout, sections: SectionConfig[], seoKey?, featureFlag?, visible? }`. Each
section holds ordered `widgets` with `props` (see mock §1.5 for a full
hero/categories/product-collection example). This is the builder's output.
- **`staticPages`** (`StaticPagesConfig`, `static-page.model.ts`, optional) —
either `Record<slug, StaticPageConfig>` or a legacy array. Each page carries
localized `title` + `html`/`content`, `route`, `seo`, `status:
'draft'|'published'`, `enabled`, `visibility`, `updatedAt`, etc. Storefront
legal/content pages live here (about-us, privacy-policy, terms-of-service in
the mock). Content-management edits operate on this in-memory (BACKEND-AUDIT
§16) — no dedicated content backend.
- **`widgetRegistry`** (`WidgetRegistryConfig`, optional) —
`{ manifestUrl: string }`; the URL `WidgetManifestService` fetches the widget
manifest from (§2 "Widget manifest" endpoint).
### 1.4 Versioning
`BootstrapConfig.schemaVersion` (string) **does** exist and is the schema/version
field. Current usage:
- The mock document sets `"schemaVersion": "1.0.0"`.
- `bootstrap-diagnostics.validator.ts:44` surfaces it as a diagnostic entry
(`['schemaVersion', bootstrap.schemaVersion]`).
- `AdminDashboardFacade` (`admin-dashboard.facade.ts:110`) derives the
`dashboard.healthBootstrapValid` health check purely as
`healthy: !!current?.schemaVersion` — i.e. "bootstrap is valid" today means
only "a schemaVersion string is present," **not** any real semantic-version
compatibility check.
> **Requires backend decision:** whether `schemaVersion` should be enforced
> (frontend rejecting/ warning on an unknown major version). No such
> enforcement exists today — any string is accepted, and only its presence is
> checked.
### 1.5 Example JSON
Realistic example, taken from the actual mock document
`src/assets/mock/bootstrap/bootstrap.json` (abridged — pages/staticPages
trimmed for length; full versions in that file):
```json
{
"schemaVersion": "1.0.0",
"generatedAt": "2026-07-03T00:00:00Z",
"tenant": {
"id": "tenant-default-001",
"slug": "default",
"code": "DEFAULT",
"host": "default.local",
"name": "Marketplace",
"websiteBaseUrl": "https://marketplace.local",
"builderBaseUrl": "https://builder.marketplace.local",
"backofficeBaseUrl": "https://backoffice.marketplace.local",
"defaultLocale": "ru",
"supportedLocales": ["ru", "en", "hy"],
"defaultCurrency": "RUB",
"supportedCurrencies": ["RUB", "USD", "EUR", "AMD"],
"timezone": "Europe/Moscow"
},
"branding": {
"brandName": "Marketplace",
"legalName": "Marketplace LLC",
"slogan": "Digital commerce marketplace",
"logoUrl": "/icons/icon-192x192.png",
"faviconUrl": "/favicon.ico",
"supportEmail": "support@marketplace.local",
"supportPhone": "+7-900-000-00-00"
},
"theme": {
"themeId": "default-light",
"mode": "light",
"palette": {
"primary": "#497671", "secondary": "#a1b4b5", "accent": "#a7ceca",
"success": "#10b981", "warning": "#f59e0b", "danger": "#ef4444",
"info": "#3b82f6", "textPrimary": "#1e3c38", "textSecondary": "#667a77",
"backgroundPrimary": "#ffffff", "backgroundSecondary": "#f5f5f5",
"border": "#d3dad9"
},
"typography": { "primaryFontFamily": "DM Sans, sans-serif", "baseFontSize": 16 },
"spacing": { "unit": 4, "scale": [0, 4, 8, 12, 16, 24, 32, 48] },
"borderRadiusScale": { "sm": "8px", "md": "12px", "lg": "16px", "xl": "22px" },
"shadows": { "sm": "0 2px 8px rgba(0,0,0,0.1)" },
"iconSet": "default"
},
"company": {
"companyName": "Marketplace LLC",
"registrationNumber": "1027700000000",
"taxId": "7700000000",
"address": { "country": "Russia", "region": "Moscow", "city": "Moscow", "street": "Tverskaya 1", "postalCode": "125009" },
"contacts": { "email": "support@marketplace.local", "phone": "+7-900-000-00-00", "telegram": "@marketplace_support", "website": "https://marketplace.local" }
},
"featureFlags": {
"wishlist": true, "compare": true, "reviews": true, "blog": false,
"chat": false, "analytics": true, "notifications": true, "coupons": true,
"loyalty": false, "giftCards": false, "invoices": true
},
"apiEndpoints": {
"bootstrap": { "path": "/bootstrap", "method": "GET", "timeoutMs": 10000 },
"website": {}, "builder": {}, "backoffice": {}
},
"localization": {
"defaultLocale": "ru",
"supportedLocales": ["ru", "en", "hy"],
"currencyByLocale": { "ru": "RUB", "en": "USD", "hy": "AMD" },
"dictionaries": [
{ "locale": "ru", "dictionaryUrl": "/assets/i18n/ru.json", "version": "1.0.0" },
{ "locale": "en", "dictionaryUrl": "/assets/i18n/en.json", "version": "1.0.0" },
{ "locale": "hy", "dictionaryUrl": "/assets/i18n/hy.json", "version": "1.0.0" }
]
},
"seo": {
"default": { "title": "Marketplace", "description": "Digital commerce marketplace", "robots": "index,follow" },
"byPageKey": { "home": { "title": "Marketplace - Home", "canonicalUrl": "https://marketplace.local/", "robots": "index,follow" } }
},
"permissions": {
"definitions": [
{ "key": "builder.pages.edit", "description": "Edit pages in builder" },
{ "key": "backoffice.products.read", "description": "Read products in backoffice" }
],
"roles": [
{ "role": "builder_admin", "permissions": ["builder.pages.edit"] },
{ "role": "backoffice_manager", "permissions": ["backoffice.products.read"] }
]
},
"catalog": {
"layout": "grid", "navigationMode": "default", "defaultSort": "relevance",
"availableSorts": ["relevance", "latest", "price_asc", "price_desc", "rating", "popular", "discount"],
"enabledFilters": ["price", "availability", "rating", "brand", "category", "subcategory", "discount", "new", "color", "size", "attributes"],
"showBreadcrumbs": true, "showRatings": true, "showDiscounts": true, "showAvailability": true
},
"productPage": {
"rating": { "enabled": true },
"reviews": { "enabled": true, "pageSize": 5, "showSummary": true },
"questions": { "enabled": true, "pageSize": 5 },
"relatedProducts": { "enabled": true }
},
"navigation": {
"header": [
{ "id": "nav-home", "labelKey": "nav.home", "route": "/", "icon": "home", "order": 1 },
{ "id": "nav-search", "labelKey": "nav.search", "route": "/search", "icon": "search", "order": 2 }
],
"footer": [
{ "id": "footer-about", "labelKey": "nav.about", "route": "/about-us", "order": 1 }
]
},
"footer": {
"copyrightText": { "ru": "© 2026 Marketplace. Все права защищены.", "en": "© 2026 Marketplace. All rights reserved." },
"legalPageKeys": ["about-us", "privacy-policy", "terms-of-service"]
},
"widgetRegistry": { "manifestUrl": "/assets/mock/bootstrap/widget-manifest.json" },
"staticPages": {
"about-us": {
"route": "/about-us",
"title": { "ru": "О компании", "en": "About Us", "hy": "Մեր մասին" },
"html": { "ru": "<h2>О компании</h2><p>…</p>", "en": "<h2>About Us</h2><p>…</p>" }
}
},
"pages": [
{
"id": "page-home", "key": "home", "title": "Home",
"route": { "path": "/", "exact": true },
"layout": { "type": "default" }, "seoKey": "home", "visible": true,
"sections": [
{
"id": "section-hero", "type": "hero", "order": 1,
"layout": { "strategy": "hero", "columns": 1, "gap": "1.5rem", "align": "stretch" },
"visible": true,
"widgets": [
{
"id": "widget-hero-main", "type": "hero", "version": "1.0.0", "order": 1,
"props": {
"title": { "ru": "Добро пожаловать на Маркетплейс", "en": "Welcome to Marketplace Platform" },
"ctaLabel": { "ru": "Начать покупки", "en": "Start Shopping" }
}
}
]
}
]
}
]
}
```
### 1.6 Lifecycle
- There is **no `APP_INITIALIZER`** wiring bootstrap fetch as a hard app
precondition. `main.ts` calls `bootstrapApplication(App, appConfig)` with no
docs: finalize BACKEND.md as a self-contained implementation prompt Goal: this document alone, pasted into a fresh Claude session, should be enough for a backend dev to implement against without getting stuck or inventing conventions the frontend doesn't actually need. Fixed a real bug: ~65 cross-references throughout the document pointed to docs/AUTHENTICATION.md, docs/ERROR_CONTRACT.md, docs/MAINTENANCE_MODE.md - three sibling docs that were deleted and fully merged into this document's own §4/§6/§10 during the earlier doc-consolidation pass, but the in-text references were never updated. A fresh agent following those links would hit dead ends repeatedly. Bulk-replaced with in- document section references; hand-fixed ~4 sentences that framed §6/ §10 as "sibling task, in progress" (stale - both are complete, this document's §6/§10 already are the settled contract, nothing to wait on). Added "Recommended default" to every item across the document's 5 consolidated "Requires backend decision" registers (§2.12 framework, §3.21 CRUD cross-cutting - 18 items, §4 §12 auth open items - 9 items, §6 error-model summary - 9 items, §10 maintenance-mode list - 5 items). Each default is derived from what the frontend already implies or standard REST/security convention - no invented APIs or business rules. ~6 items are explicitly flagged as real business/security decisions instead (order state-machine rules, Ed25519 cutover strategy, refresh-token reuse-detection posture) since those carry consequences no amount of frontend-code-reading can resolve. Added a "How to use this document" preamble up front: work in §9's dependency order not document order, apply recommended defaults and keep moving, only stop for the explicitly-flagged business/security items, don't invent beyond what's written or directly implied. Frontend side: confirmed nothing else is left. docs/TODO.md already has zero blockers; docs/KNOWN-ISSUES.md's one open item (unreachable Ed25519 error-code UI) is correctly left open and documented rather than faked closed - fixing it needs a real backend emitting real distinguishable error codes, which doesn't exist yet and can't be fabricated without inventing an API contract. No frontend code touched. No architecture changed. No APIs invented.
2026-07-26 16:10:33 +04:00
initializer that blocks on config. (`§4 (Authentication, this document)` §5.2 separately
notes the Ed25519 `restoreSession()` initializer is also not wired.)
- Bootstrap is instead loaded lazily-but-eagerly by the first consumer to need
it. Multiple root-level services subscribe to `ConfigService.loadBootstrap()`
at construction: `UiRuntimeFacade` (`ui-runtime.facade.ts:32`),
`PlatformRuntimeService`, `ThemeEngineService`, `BrandingEngineService`, the
`FooterComponent`, etc. Because `ConfigService` de-dupes (§1.7), the first
one triggers the single HTTP call and the rest share it.
- The `languageGuard` (`src/app/guards/language.guard.ts`, wrapping every
`:lang` route) runs on navigation and preloads the locale dictionary; it is
**not** itself the bootstrap fetcher, but it runs in the same startup window.
Bootstrap and language resolution are effectively concurrent — the app does
not gate rendering on bootstrap completion, so components must tolerate a
transient null snapshot.
### 1.7 Caching behavior (what the frontend actually does)
`ConfigService` (`src/app/core/config/config.service.ts`) is the cache, and it
is **in-memory only** — no `localStorage`, no HTTP cache header logic:
- Holds `bootstrapSnapshot: BootstrapConfig | null` (last successful value) and
a shared `bootstrap$` observable with `shareReplay(1)`.
- `loadBootstrap(forceRefresh = false)`: returns the cached `bootstrap$` when a
snapshot exists and `forceRefresh` is false; otherwise (re)issues the
provider call.
- On error, it clears both `bootstrap$` and `bootstrapSnapshot` so the next
call retries cleanly (`catchError` rethrows).
- A `bootstrapRevision` signal increments on every successful load/override —
consumers use it to react to config changes.
- `getBootstrapSnapshot()` returns the current in-memory value synchronously
(used by `WidgetManifestService`, diagnostics, etc.).
- The cache does **not** survive a page reload — every full page load re-fetches
bootstrap. There is also a separate client-side `cacheInterceptor`
(GET-response caching, BACKEND-AUDIT §3) that may cache the `/bootstrap`
response at the HTTP layer within a session, but nothing persists it across
sessions.
### 1.8 ETag / conditional requests
**Requires backend decision — no ETag / conditional-request logic exists in
the frontend today.** The bootstrap provider issues a plain unconditional
`GET`; nothing reads or sends `ETag`, `If-None-Match`, `Last-Modified`, or
`If-Modified-Since`. If the backend wants conditional revalidation of the
(large, per-tenant, infrequently changing) bootstrap document, both the wire
strategy and the frontend handling for it would be new work.
### 1.9 Tenant resolution
Tenant is resolved by **subdomain**, not by header or path (full detail in
docs: finalize BACKEND.md as a self-contained implementation prompt Goal: this document alone, pasted into a fresh Claude session, should be enough for a backend dev to implement against without getting stuck or inventing conventions the frontend doesn't actually need. Fixed a real bug: ~65 cross-references throughout the document pointed to docs/AUTHENTICATION.md, docs/ERROR_CONTRACT.md, docs/MAINTENANCE_MODE.md - three sibling docs that were deleted and fully merged into this document's own §4/§6/§10 during the earlier doc-consolidation pass, but the in-text references were never updated. A fresh agent following those links would hit dead ends repeatedly. Bulk-replaced with in- document section references; hand-fixed ~4 sentences that framed §6/ §10 as "sibling task, in progress" (stale - both are complete, this document's §6/§10 already are the settled contract, nothing to wait on). Added "Recommended default" to every item across the document's 5 consolidated "Requires backend decision" registers (§2.12 framework, §3.21 CRUD cross-cutting - 18 items, §4 §12 auth open items - 9 items, §6 error-model summary - 9 items, §10 maintenance-mode list - 5 items). Each default is derived from what the frontend already implies or standard REST/security convention - no invented APIs or business rules. ~6 items are explicitly flagged as real business/security decisions instead (order state-machine rules, Ed25519 cutover strategy, refresh-token reuse-detection posture) since those carry consequences no amount of frontend-code-reading can resolve. Added a "How to use this document" preamble up front: work in §9's dependency order not document order, apply recommended defaults and keep moving, only stop for the explicitly-flagged business/security items, don't invent beyond what's written or directly implied. Frontend side: confirmed nothing else is left. docs/TODO.md already has zero blockers; docs/KNOWN-ISSUES.md's one open item (unreachable Ed25519 error-code UI) is correctly left open and documented rather than faked closed - fixing it needs a real backend emitting real distinguishable error codes, which doesn't exist yet and can't be fabricated without inventing an API contract. No frontend code touched. No architecture changed. No APIs invented.
2026-07-26 16:10:33 +04:00
`§4 (Authentication, this document)` §10; source
`src/app/core/config/tenant-resolver.service.ts`):
- `TenantResolverService.getTenantKey()`: localhost →
`environment.fallbackTenantKey ?? 'default'`; otherwise the **first DNS
label** of the hostname, skipping a leading `www` (e.g.
`dexarmarket.api.dexarmarket.ru``dexarmarket`; `www.acme.com``acme`).
- That tenant key feeds `ApiConfigService.getBaseUrl()`, which picks the
marketplace API base: localhost → `environment.localhostApiUrl` (`/api`);
else `environment.tenantApiBaseUrls[tenantKey]`; else
`environment.tenantApiTemplate` with `{tenant}` substituted; else (only when
`allowBootstrapApiOverride` is true — off by default) a value read out of the
already-loaded bootstrap (`apiEndpoints.website.baseUrl` /
`tenant.apiBaseUrl`).
- **No `X-Tenant` header or `/tenant/{id}` path prefix is ever sent.** Tenant
isolation for `GET /bootstrap` is achieved purely by *which origin/subdomain
the request goes to* — the backend must infer the tenant from the request
host, and return that tenant's bootstrap document.
### 1.10 Draft vs Published
The storefront and the builder read the **same** in-memory `BootstrapConfig`;
there is no separate "draft bootstrap" vs "live bootstrap" endpoint today
(BACKEND-AUDIT §17):
- The builder (`ProjectEditorFacade`,
`src/app/features/project-editor/facade/project-editor.facade.ts`) loads the
bootstrap via `ConfigService.loadBootstrap(true)` (force refresh), edits an
in-memory `History<BootstrapConfig>` (undo/redo), and persists **drafts to
localStorage** through `ProjectEditorDraftStorageService`.
- `ConfigService.applyBootstrapOverride(next)` swaps the in-memory snapshot
(deep-cloned) so a preview reflects edits immediately — but this is purely
client-side.
- **There is no publish/write HTTP call.** No `PUT /bootstrap`, no
builder-write endpoint exists anywhere in code. "Publishing a marketplace"
(writing the edited bootstrap back so the live storefront serves it) is
**FUTURE / LOCAL-ONLY today**. Any builder publish endpoint would be declared
under `apiEndpoints.builder` (runtime-declared) and is a backend build item,
not something the frontend currently calls.
> **Requires backend decision:** the entire draft→publish write path
> (endpoint, method, optimistic-concurrency / version check on save, and
> whether draft state is server-persisted or stays localStorage-only).
### 1.11 Fallback behavior (bootstrap fails to load)
- `ConfigService` does **not** substitute a default document on failure — it
clears its cache and rethrows, so each subscribing consumer sees the error.
- There is no global "bootstrap failed" full-screen error page. Instead,
individual engines degrade: `ThemeEngineService`/`BrandingEngineService`
subscribe with `take(1)` and simply do nothing extra on error (the app keeps
its compiled default styles); optional config getters fall back to hard-coded
defaults (`DEFAULT_CATALOG_CONFIG`, `DEFAULT_USER_EXPERIENCE_CONFIG`);
`WidgetManifestService` falls back to the static manifest URL on any bootstrap
error.
- Feature-specific consumers that require bootstrap data render their own
generic empty/error states (the same `common.errorTitle` pattern described in
docs: finalize BACKEND.md as a self-contained implementation prompt Goal: this document alone, pasted into a fresh Claude session, should be enough for a backend dev to implement against without getting stuck or inventing conventions the frontend doesn't actually need. Fixed a real bug: ~65 cross-references throughout the document pointed to docs/AUTHENTICATION.md, docs/ERROR_CONTRACT.md, docs/MAINTENANCE_MODE.md - three sibling docs that were deleted and fully merged into this document's own §4/§6/§10 during the earlier doc-consolidation pass, but the in-text references were never updated. A fresh agent following those links would hit dead ends repeatedly. Bulk-replaced with in- document section references; hand-fixed ~4 sentences that framed §6/ §10 as "sibling task, in progress" (stale - both are complete, this document's §6/§10 already are the settled contract, nothing to wait on). Added "Recommended default" to every item across the document's 5 consolidated "Requires backend decision" registers (§2.12 framework, §3.21 CRUD cross-cutting - 18 items, §4 §12 auth open items - 9 items, §6 error-model summary - 9 items, §10 maintenance-mode list - 5 items). Each default is derived from what the frontend already implies or standard REST/security convention - no invented APIs or business rules. ~6 items are explicitly flagged as real business/security decisions instead (order state-machine rules, Ed25519 cutover strategy, refresh-token reuse-detection posture) since those carry consequences no amount of frontend-code-reading can resolve. Added a "How to use this document" preamble up front: work in §9's dependency order not document order, apply recommended defaults and keep moving, only stop for the explicitly-flagged business/security items, don't invent beyond what's written or directly implied. Frontend side: confirmed nothing else is left. docs/TODO.md already has zero blockers; docs/KNOWN-ISSUES.md's one open item (unreachable Ed25519 error-code UI) is correctly left open and documented rather than faked closed - fixing it needs a real backend emitting real distinguishable error codes, which doesn't exist yet and can't be fabricated without inventing an API contract. No frontend code touched. No architecture changed. No APIs invented.
2026-07-26 16:10:33 +04:00
`§6 (Error Model, this document)`), not a bootstrap-specific one.
> **Requires backend decision / frontend follow-up:** whether a hard bootstrap
> failure should present a dedicated tenant-level error screen. Today the
> failure is silent-ish (default theme, empty dynamic pages) rather than a
> blocking error, which is a UX gap if the backend can actually return a fatal
> bootstrap error.
### 1.12 Failure responses
The frontend does not parse a bootstrap-specific error body — a failed
`GET /bootstrap` surfaces as a bare `HttpErrorResponse` handled per §1.11. For
the standard error envelope every endpoint (including this one) should return
on non-2xx, and the per-status semantics (401/403/404/409/422/429/500/503,
plus `TENANT_DISABLED` and `MAINTENANCE_MODE`), see
docs: finalize BACKEND.md as a self-contained implementation prompt Goal: this document alone, pasted into a fresh Claude session, should be enough for a backend dev to implement against without getting stuck or inventing conventions the frontend doesn't actually need. Fixed a real bug: ~65 cross-references throughout the document pointed to docs/AUTHENTICATION.md, docs/ERROR_CONTRACT.md, docs/MAINTENANCE_MODE.md - three sibling docs that were deleted and fully merged into this document's own §4/§6/§10 during the earlier doc-consolidation pass, but the in-text references were never updated. A fresh agent following those links would hit dead ends repeatedly. Bulk-replaced with in- document section references; hand-fixed ~4 sentences that framed §6/ §10 as "sibling task, in progress" (stale - both are complete, this document's §6/§10 already are the settled contract, nothing to wait on). Added "Recommended default" to every item across the document's 5 consolidated "Requires backend decision" registers (§2.12 framework, §3.21 CRUD cross-cutting - 18 items, §4 §12 auth open items - 9 items, §6 error-model summary - 9 items, §10 maintenance-mode list - 5 items). Each default is derived from what the frontend already implies or standard REST/security convention - no invented APIs or business rules. ~6 items are explicitly flagged as real business/security decisions instead (order state-machine rules, Ed25519 cutover strategy, refresh-token reuse-detection posture) since those carry consequences no amount of frontend-code-reading can resolve. Added a "How to use this document" preamble up front: work in §9's dependency order not document order, apply recommended defaults and keep moving, only stop for the explicitly-flagged business/security items, don't invent beyond what's written or directly implied. Frontend side: confirmed nothing else is left. docs/TODO.md already has zero blockers; docs/KNOWN-ISSUES.md's one open item (unreachable Ed25519 error-code UI) is correctly left open and documented rather than faked closed - fixing it needs a real backend emitting real distinguishable error codes, which doesn't exist yet and can't be fabricated without inventing an API contract. No frontend code touched. No architecture changed. No APIs invented.
2026-07-26 16:10:33 +04:00
**§6 (Error Model)** — that section owns the error envelope and this
section does not redefine it. Two of its rows are especially relevant to
bootstrap:
- **`TENANT_DISABLED`** (HTTP 403, `error.code: "TENANT_DISABLED"`) — the
natural failure for `GET /bootstrap` against a known-but-inactive tenant.
docs: finalize BACKEND.md as a self-contained implementation prompt Goal: this document alone, pasted into a fresh Claude session, should be enough for a backend dev to implement against without getting stuck or inventing conventions the frontend doesn't actually need. Fixed a real bug: ~65 cross-references throughout the document pointed to docs/AUTHENTICATION.md, docs/ERROR_CONTRACT.md, docs/MAINTENANCE_MODE.md - three sibling docs that were deleted and fully merged into this document's own §4/§6/§10 during the earlier doc-consolidation pass, but the in-text references were never updated. A fresh agent following those links would hit dead ends repeatedly. Bulk-replaced with in- document section references; hand-fixed ~4 sentences that framed §6/ §10 as "sibling task, in progress" (stale - both are complete, this document's §6/§10 already are the settled contract, nothing to wait on). Added "Recommended default" to every item across the document's 5 consolidated "Requires backend decision" registers (§2.12 framework, §3.21 CRUD cross-cutting - 18 items, §4 §12 auth open items - 9 items, §6 error-model summary - 9 items, §10 maintenance-mode list - 5 items). Each default is derived from what the frontend already implies or standard REST/security convention - no invented APIs or business rules. ~6 items are explicitly flagged as real business/security decisions instead (order state-machine rules, Ed25519 cutover strategy, refresh-token reuse-detection posture) since those carry consequences no amount of frontend-code-reading can resolve. Added a "How to use this document" preamble up front: work in §9's dependency order not document order, apply recommended defaults and keep moving, only stop for the explicitly-flagged business/security items, don't invent beyond what's written or directly implied. Frontend side: confirmed nothing else is left. docs/TODO.md already has zero blockers; docs/KNOWN-ISSUES.md's one open item (unreachable Ed25519 error-code UI) is correctly left open and documented rather than faked closed - fixing it needs a real backend emitting real distinguishable error codes, which doesn't exist yet and can't be fabricated without inventing an API contract. No frontend code touched. No architecture changed. No APIs invented.
2026-07-26 16:10:33 +04:00
`§6` marks the frontend handling for this as "Requires backend
decision" (no code path handles it today).
- **`MAINTENANCE_MODE`** (HTTP 503, `error.code: "MAINTENANCE_MODE"`) — if a
tenant is down for maintenance, bootstrap is where the frontend would first
hit it. Again, no maintenance concept exists frontend-side today.
---
## 2. Endpoint Documentation Framework
This section defines the **general contract every endpoint in the system
follows**, so the reader has the framework before the per-domain endpoint
catalog (a separate section). Where the frontend does not define something,
it is marked **"Requires backend decision"** rather than invented.
### 2.1 Standard success response envelope
**There is no `ApiResponse<T>` / `{ success, data, error }` envelope in this
codebase.** Every live caller types its `HttpClient` call to the **bare payload
type** and consumes the raw JSON directly:
- `ApiService.getItem()``HttpClient.get<Item>(...)` (the item object itself).
- `ApiService.getCategories()``get<Category[]>` (bare array).
- `ApiBootstrapProvider``get<BootstrapConfig>` (bare object).
- `TelegramSessionApiService``get<Record<string, unknown>>` then normalizes.
The generic `ApiResponse<T>` shape in the org coding-standards is **not** used
here — do not assume an envelope. Success responses are the raw resource
(object, array, or `{ items, total }` for lists — see §2.2).
> **Requires backend decision:** whether to *introduce* a success envelope
> going forward. If adopted it would be a breaking change to every existing
> live caller listed in §2.6 and BACKEND-AUDIT §4, each of which currently
> expects the bare payload. Recommendation: keep bare payloads for the existing
> live endpoints; only new endpoints could opt into an envelope, and even then
> the frontend has no envelope-unwrapping layer today (one would be new work).
### 2.2 Standard pagination contract
Two distinct pagination shapes already exist in the frontend; a backend should
align to these rather than invent a third:
**A) Offset/count style (marketplace list + search endpoints)** — used by the
live `ApiService`:
- Request query params: **`count`** (page size, default 50) and **`skip`**
(offset, default 0). Applied on `GET /category/{id}`, `GET /searchitems`,
`GET /items/randomitems`.
- Response for `searchItems`: `{ items: Item[], total: number }`. Other list
endpoints (`getCategoryItems`, `getRandomItems`) return a **bare array** with
no total.
**B) Page/pageSize style (admin lists, storefront engagement lists, media)** —
used by facade/gateway list results:
- Request: `{ page: number, pageSize: number }` (plus filters — §2.4). See
`AdminOrderListFilters`, `EngagementListQuery`, `MediaListParams`.
- Response: `{ items: T[], total: number, page: number, pageSize: number }`.
Canonical shapes: `EngagementListResult<T>`
(`core/products/models/product-engagement.model.ts:55`), `AdminOrdersListResult`
(`features/admin/orders/models/admin-order.model.ts:63`), `MediaListResult`
(`{ items, total }` only). The UI derives `totalPages = ceil(total /
pageSize)` client-side (e.g. `pagination.component.ts`, admin list pages).
Notes / open items:
- The two styles use different param names (`count`/`skip` vs `page`/`pageSize`)
— a backend serving both storefront and admin must support both, or a
reconciliation decision is needed.
- **No cursor/keyset pagination anywhere.** The only `cursor` matches in source
are CSS `cursor:` properties. There is no `nextCursor`/`hasMore` on the wire
(`catalog-state.model.ts` has a client-derived `hasMore` for infinite-scroll,
computed as `skip + pageSize < total`, not a backend field).
> **Requires backend decision:** exact page-size **limits/maximums** (the
> frontend sends 50 as a default `count` but enforces no server-side cap),
> whether to standardize on offset vs page-number, and whether large lists
> should move to cursor pagination (nothing in the frontend implies or requires
> cursors today).
### 2.3 Standard sorting contract
Sorting is **enumerated in the bootstrap `catalog` config**, not free-form:
- `CatalogConfig.availableSorts` / `CatalogConfig.defaultSort` (see §1.3,
`catalog-config.model.ts`) enumerate the allowed values:
`relevance | latest | price_asc | price_desc | rating | popular | discount`.
- The live wire param is `sort` on `GET /searchitems`
(`ApiService.searchItems`), whose accepted set is a subset:
`relevance | price_asc | price_desc | popular | rating` (the code's
`options.sort` union). `latest`/`discount` are catalog-config values without a
confirmed search-endpoint mapping.
- Admin lists sort primarily via their filter objects / client-side; there is
no shared admin `sort` query-param convention on the wire yet (admin CRUD is
mock-only, BACKEND-AUDIT §14).
> **Requires backend decision:** reconcile the catalog-config sort vocabulary
> (7 values) with the search-endpoint `sort` vocabulary (5 values) — and define
> the wire encoding for admin list sorting (field + direction) when those
> endpoints are built.
### 2.4 Standard filtering contract
Filtering shapes already used by the frontend:
- **Storefront search/catalog** — `GET /searchitems` accepts optional
`categoryIDs` (comma-joined ints), `minPrice`, `maxPrice`, `tag`, plus `sort`
(§2.3). `CatalogConfig.enabledFilters` declares which filter UIs a tenant
exposes (`price, availability, rating, brand, category, subcategory, discount,
new, color, size, attributes`).
- **Admin lists** — each domain has a filters object, all following a
`{ search: string, <field>: 'all' | <enum>, page, pageSize }` shape. Examples:
- `AdminOrderListFilters` = `{ search, status: 'all'|AdminOrderStatus, page,
pageSize }`.
- `AdminMonitoringEventFilters` = `{ category: 'all'|AdminMonitoringCategory,
search }` (note: no page/pageSize on this one).
- `MediaListParams` = `{ page?, pageSize?, search?, folder?, tag?, kind?,
sort? }`.
The convention: a text `search` string plus enum facets where the sentinel
**`'all'`** means "no filter on this facet."
> **Requires backend decision:** the wire encoding of these filters as query
> params for the (not-yet-built) admin endpoints — the shapes above are
> in-memory facade filter objects, not confirmed query-string contracts. Also
> whether `'all'` is sent literally or omitted to mean unfiltered.
### 2.5 Standard search contract
- Live full-text search: `GET /searchitems?search=<q>&count=&skip=[&categoryIDs
&minPrice&maxPrice&tag&sort]` → `{ items: Item[], total: number }`
(`ApiService.searchItems`). The query param is **`search`**.
- The `SearchFacade` (BACKEND-AUDIT §18) is a **client-side orchestration** over
`ProductFacade`/`CategoryFacade` — history, trending, autocomplete, and cache
are all localStorage/in-memory. There is **no dedicated search backend
endpoint** beyond `/searchitems`; autocomplete/suggestions are derived
client-side today.
> **Requires backend decision:** whether server-side autocomplete/suggestion
> and trending endpoints are wanted (nothing on the wire today), and the
> min-query-length / debounce contract if so.
### 2.6 Nullable-field convention
The frontend does **not** ascribe distinct semantics to `null` vs `undefined`
vs an omitted key on the wire — it treats all three as "absent" and applies
defaults defensively:
- Model interfaces mark optional fields with `?` (implying may be omitted);
a handful use explicit `T | null` (e.g. `AuthSession.userId: number | null`,
`AuthSession.username: string | null` where `null` specifically means "known
to be absent").
- The tolerant normalizers (`ApiService.normalizeItem/normalizeCategory`,
`TelegramSessionApiService.normalizeWebSession`) coalesce `null`/`undefined`/
missing uniformly via `??` and default fallbacks — they never branch on
`null` vs `undefined`.
- **Convention to follow:** the backend may omit an optional key or send `null`
interchangeably; both are handled. Explicit `null` is only meaningful where a
model types the field as `… | null` to signal "definitively no value" (auth
user id/username being the notable examples).
### 2.7 JWT / authentication header format
docs: finalize BACKEND.md as a self-contained implementation prompt Goal: this document alone, pasted into a fresh Claude session, should be enough for a backend dev to implement against without getting stuck or inventing conventions the frontend doesn't actually need. Fixed a real bug: ~65 cross-references throughout the document pointed to docs/AUTHENTICATION.md, docs/ERROR_CONTRACT.md, docs/MAINTENANCE_MODE.md - three sibling docs that were deleted and fully merged into this document's own §4/§6/§10 during the earlier doc-consolidation pass, but the in-text references were never updated. A fresh agent following those links would hit dead ends repeatedly. Bulk-replaced with in- document section references; hand-fixed ~4 sentences that framed §6/ §10 as "sibling task, in progress" (stale - both are complete, this document's §6/§10 already are the settled contract, nothing to wait on). Added "Recommended default" to every item across the document's 5 consolidated "Requires backend decision" registers (§2.12 framework, §3.21 CRUD cross-cutting - 18 items, §4 §12 auth open items - 9 items, §6 error-model summary - 9 items, §10 maintenance-mode list - 5 items). Each default is derived from what the frontend already implies or standard REST/security convention - no invented APIs or business rules. ~6 items are explicitly flagged as real business/security decisions instead (order state-machine rules, Ed25519 cutover strategy, refresh-token reuse-detection posture) since those carry consequences no amount of frontend-code-reading can resolve. Added a "How to use this document" preamble up front: work in §9's dependency order not document order, apply recommended defaults and keep moving, only stop for the explicitly-flagged business/security items, don't invent beyond what's written or directly implied. Frontend side: confirmed nothing else is left. docs/TODO.md already has zero blockers; docs/KNOWN-ISSUES.md's one open item (unreachable Ed25519 error-code UI) is correctly left open and documented rather than faked closed - fixing it needs a real backend emitting real distinguishable error codes, which doesn't exist yet and can't be fabricated without inventing an API contract. No frontend code touched. No architecture changed. No APIs invented.
2026-07-26 16:10:33 +04:00
Two coexisting mechanisms; full spec in **`§4 (Authentication, this document)`** (this
section only references it, does not redefine):
- **Telegram session auth (LIVE)** — no `Authorization: Bearer`. Identity is a
**`WebSessionID`** header (auth session id, or a persisted anonymous 32-hex id
from `localStorage['web_session_id']`) attached to marketplace API requests by
`apiHeadersInterceptor`. Admin requests additionally carry an
**`AdminWebSessionID`** header (`adminAuthHeadersInterceptor`, gating URL
segments `/admin/`, `/backoffice/`, `/builder/`, `/media/`).
- **Ed25519 admin auth (wired, backend absent)** — issues a JWT
`AuthTokenPair { token, refreshToken }`; the intended header is standard
`Authorization: Bearer <token>`, but its `authInterceptor` is **not
docs: finalize BACKEND.md as a self-contained implementation prompt Goal: this document alone, pasted into a fresh Claude session, should be enough for a backend dev to implement against without getting stuck or inventing conventions the frontend doesn't actually need. Fixed a real bug: ~65 cross-references throughout the document pointed to docs/AUTHENTICATION.md, docs/ERROR_CONTRACT.md, docs/MAINTENANCE_MODE.md - three sibling docs that were deleted and fully merged into this document's own §4/§6/§10 during the earlier doc-consolidation pass, but the in-text references were never updated. A fresh agent following those links would hit dead ends repeatedly. Bulk-replaced with in- document section references; hand-fixed ~4 sentences that framed §6/ §10 as "sibling task, in progress" (stale - both are complete, this document's §6/§10 already are the settled contract, nothing to wait on). Added "Recommended default" to every item across the document's 5 consolidated "Requires backend decision" registers (§2.12 framework, §3.21 CRUD cross-cutting - 18 items, §4 §12 auth open items - 9 items, §6 error-model summary - 9 items, §10 maintenance-mode list - 5 items). Each default is derived from what the frontend already implies or standard REST/security convention - no invented APIs or business rules. ~6 items are explicitly flagged as real business/security decisions instead (order state-machine rules, Ed25519 cutover strategy, refresh-token reuse-detection posture) since those carry consequences no amount of frontend-code-reading can resolve. Added a "How to use this document" preamble up front: work in §9's dependency order not document order, apply recommended defaults and keep moving, only stop for the explicitly-flagged business/security items, don't invent beyond what's written or directly implied. Frontend side: confirmed nothing else is left. docs/TODO.md already has zero blockers; docs/KNOWN-ISSUES.md's one open item (unreachable Ed25519 error-code UI) is correctly left open and documented rather than faked closed - fixing it needs a real backend emitting real distinguishable error codes, which doesn't exist yet and can't be fabricated without inventing an API contract. No frontend code touched. No architecture changed. No APIs invented.
2026-07-26 16:10:33 +04:00
registered** today (§4 §2.6), so no request auto-attaches the
bearer token yet. `adminAuthHeadersInterceptor` *does* set `Authorization:
Bearer <token>` if an admin token happens to be stored, but nothing stores one
in the live flow.
docs: finalize BACKEND.md as a self-contained implementation prompt Goal: this document alone, pasted into a fresh Claude session, should be enough for a backend dev to implement against without getting stuck or inventing conventions the frontend doesn't actually need. Fixed a real bug: ~65 cross-references throughout the document pointed to docs/AUTHENTICATION.md, docs/ERROR_CONTRACT.md, docs/MAINTENANCE_MODE.md - three sibling docs that were deleted and fully merged into this document's own §4/§6/§10 during the earlier doc-consolidation pass, but the in-text references were never updated. A fresh agent following those links would hit dead ends repeatedly. Bulk-replaced with in- document section references; hand-fixed ~4 sentences that framed §6/ §10 as "sibling task, in progress" (stale - both are complete, this document's §6/§10 already are the settled contract, nothing to wait on). Added "Recommended default" to every item across the document's 5 consolidated "Requires backend decision" registers (§2.12 framework, §3.21 CRUD cross-cutting - 18 items, §4 §12 auth open items - 9 items, §6 error-model summary - 9 items, §10 maintenance-mode list - 5 items). Each default is derived from what the frontend already implies or standard REST/security convention - no invented APIs or business rules. ~6 items are explicitly flagged as real business/security decisions instead (order state-machine rules, Ed25519 cutover strategy, refresh-token reuse-detection posture) since those carry consequences no amount of frontend-code-reading can resolve. Added a "How to use this document" preamble up front: work in §9's dependency order not document order, apply recommended defaults and keep moving, only stop for the explicitly-flagged business/security items, don't invent beyond what's written or directly implied. Frontend side: confirmed nothing else is left. docs/TODO.md already has zero blockers; docs/KNOWN-ISSUES.md's one open item (unreachable Ed25519 error-code UI) is correctly left open and documented rather than faked closed - fixing it needs a real backend emitting real distinguishable error codes, which doesn't exist yet and can't be fabricated without inventing an API contract. No frontend code touched. No architecture changed. No APIs invented.
2026-07-26 16:10:33 +04:00
Refer to `§4 (Authentication, this document)` for token structure (`JwtClaims`), refresh,
rotation, expiry, role hierarchy, and tenant-scoping open items.
### 2.8 Live endpoint domains (real HTTP, already implemented)
The following domains have **real `HttpClient` calls in code today** (not
proposals). Documented per-endpoint below. (The Ed25519 admin-auth API is wired
to real HTTP but the backend does not implement it yet — its full contract is
docs: finalize BACKEND.md as a self-contained implementation prompt Goal: this document alone, pasted into a fresh Claude session, should be enough for a backend dev to implement against without getting stuck or inventing conventions the frontend doesn't actually need. Fixed a real bug: ~65 cross-references throughout the document pointed to docs/AUTHENTICATION.md, docs/ERROR_CONTRACT.md, docs/MAINTENANCE_MODE.md - three sibling docs that were deleted and fully merged into this document's own §4/§6/§10 during the earlier doc-consolidation pass, but the in-text references were never updated. A fresh agent following those links would hit dead ends repeatedly. Bulk-replaced with in- document section references; hand-fixed ~4 sentences that framed §6/ §10 as "sibling task, in progress" (stale - both are complete, this document's §6/§10 already are the settled contract, nothing to wait on). Added "Recommended default" to every item across the document's 5 consolidated "Requires backend decision" registers (§2.12 framework, §3.21 CRUD cross-cutting - 18 items, §4 §12 auth open items - 9 items, §6 error-model summary - 9 items, §10 maintenance-mode list - 5 items). Each default is derived from what the frontend already implies or standard REST/security convention - no invented APIs or business rules. ~6 items are explicitly flagged as real business/security decisions instead (order state-machine rules, Ed25519 cutover strategy, refresh-token reuse-detection posture) since those carry consequences no amount of frontend-code-reading can resolve. Added a "How to use this document" preamble up front: work in §9's dependency order not document order, apply recommended defaults and keep moving, only stop for the explicitly-flagged business/security items, don't invent beyond what's written or directly implied. Frontend side: confirmed nothing else is left. docs/TODO.md already has zero blockers; docs/KNOWN-ISSUES.md's one open item (unreachable Ed25519 error-code UI) is correctly left open and documented rather than faked closed - fixing it needs a real backend emitting real distinguishable error codes, which doesn't exist yet and can't be fabricated without inventing an API contract. No frontend code touched. No architecture changed. No APIs invented.
2026-07-26 16:10:33 +04:00
in `§4 (Authentication, this document)` §2, not repeated here.)
#### 2.8.1 Session auth API (LIVE)
Base: `environment.authApiUrl` (= `https://api.dexarmarket.ru:445`). Source:
`src/app/services/telegram-session-api.service.ts`. Narrative flow in
docs: finalize BACKEND.md as a self-contained implementation prompt Goal: this document alone, pasted into a fresh Claude session, should be enough for a backend dev to implement against without getting stuck or inventing conventions the frontend doesn't actually need. Fixed a real bug: ~65 cross-references throughout the document pointed to docs/AUTHENTICATION.md, docs/ERROR_CONTRACT.md, docs/MAINTENANCE_MODE.md - three sibling docs that were deleted and fully merged into this document's own §4/§6/§10 during the earlier doc-consolidation pass, but the in-text references were never updated. A fresh agent following those links would hit dead ends repeatedly. Bulk-replaced with in- document section references; hand-fixed ~4 sentences that framed §6/ §10 as "sibling task, in progress" (stale - both are complete, this document's §6/§10 already are the settled contract, nothing to wait on). Added "Recommended default" to every item across the document's 5 consolidated "Requires backend decision" registers (§2.12 framework, §3.21 CRUD cross-cutting - 18 items, §4 §12 auth open items - 9 items, §6 error-model summary - 9 items, §10 maintenance-mode list - 5 items). Each default is derived from what the frontend already implies or standard REST/security convention - no invented APIs or business rules. ~6 items are explicitly flagged as real business/security decisions instead (order state-machine rules, Ed25519 cutover strategy, refresh-token reuse-detection posture) since those carry consequences no amount of frontend-code-reading can resolve. Added a "How to use this document" preamble up front: work in §9's dependency order not document order, apply recommended defaults and keep moving, only stop for the explicitly-flagged business/security items, don't invent beyond what's written or directly implied. Frontend side: confirmed nothing else is left. docs/TODO.md already has zero blockers; docs/KNOWN-ISSUES.md's one open item (unreachable Ed25519 error-code UI) is correctly left open and documented rather than faked closed - fixing it needs a real backend emitting real distinguishable error codes, which doesn't exist yet and can't be fabricated without inventing an API contract. No frontend code touched. No architecture changed. No APIs invented.
2026-07-26 16:10:33 +04:00
`§4 (Authentication, this document)` §1.
| Endpoint | Method | Auth | Body / Headers | Response |
|---|---|---|---|---|
| `/users/sessions` | POST | none | body `{ webSessionID }` (client-generated GUID) + header `WebSessionID: <same guid>` | `{ webSessionID, url }` — normalized to `WebSessionStart`; `url` = Telegram deep link |
| `/users/sessions/{id}` | GET | none | — | Session object, field-tolerant → normalized `AuthSession` |
| `/users/sessions/{id}` | DELETE | none | header `WebSessionID: <id>` | ignored (frontend clears local state regardless) |
TypeScript interfaces (`src/app/models/auth.model.ts`):
```ts
export interface AuthSession {
sessionId: string;
userId: number | null;
username: string | null;
displayName: string;
active: boolean;
expires: string; // ISO 8601
}
export interface WebSessionStart {
webSessionID: string;
url: string;
}
```
Example — create session:
```http
POST https://api.dexarmarket.ru:445/users/sessions
WebSessionID: 3f1c2a0e-4e21-4d3a-9e77-1e8f6a2d9c11
Content-Type: application/json
{ "webSessionID": "3f1c2a0e-4e21-4d3a-9e77-1e8f6a2d9c11" }
```
```json
{
"webSessionID": "3f1c2a0e-4e21-4d3a-9e77-1e8f6a2d9c11",
"url": "https://t.me/myAMLKYCBOT?start=3f1c2a0e-4e21-4d3a-9e77-1e8f6a2d9c11"
}
```
Example — poll session (active):
```http
GET https://api.dexarmarket.ru:445/users/sessions/3f1c2a0e-4e21-4d3a-9e77-1e8f6a2d9c11
```
```json
{
"webSessionID": "3f1c2a0e-4e21-4d3a-9e77-1e8f6a2d9c11",
"status": "active",
"user": { "id": 8823771, "username": "buyer_ivan", "firstName": "Ivan", "lastName": "P" },
"expiresAt": "2026-07-26T05:00:00Z"
}
```
The frontend reads fields **field-tolerantly** (accepts many casings/aliases —
docs: finalize BACKEND.md as a self-contained implementation prompt Goal: this document alone, pasted into a fresh Claude session, should be enough for a backend dev to implement against without getting stuck or inventing conventions the frontend doesn't actually need. Fixed a real bug: ~65 cross-references throughout the document pointed to docs/AUTHENTICATION.md, docs/ERROR_CONTRACT.md, docs/MAINTENANCE_MODE.md - three sibling docs that were deleted and fully merged into this document's own §4/§6/§10 during the earlier doc-consolidation pass, but the in-text references were never updated. A fresh agent following those links would hit dead ends repeatedly. Bulk-replaced with in- document section references; hand-fixed ~4 sentences that framed §6/ §10 as "sibling task, in progress" (stale - both are complete, this document's §6/§10 already are the settled contract, nothing to wait on). Added "Recommended default" to every item across the document's 5 consolidated "Requires backend decision" registers (§2.12 framework, §3.21 CRUD cross-cutting - 18 items, §4 §12 auth open items - 9 items, §6 error-model summary - 9 items, §10 maintenance-mode list - 5 items). Each default is derived from what the frontend already implies or standard REST/security convention - no invented APIs or business rules. ~6 items are explicitly flagged as real business/security decisions instead (order state-machine rules, Ed25519 cutover strategy, refresh-token reuse-detection posture) since those carry consequences no amount of frontend-code-reading can resolve. Added a "How to use this document" preamble up front: work in §9's dependency order not document order, apply recommended defaults and keep moving, only stop for the explicitly-flagged business/security items, don't invent beyond what's written or directly implied. Frontend side: confirmed nothing else is left. docs/TODO.md already has zero blockers; docs/KNOWN-ISSUES.md's one open item (unreachable Ed25519 error-code UI) is correctly left open and documented rather than faked closed - fixing it needs a real backend emitting real distinguishable error codes, which doesn't exist yet and can't be fabricated without inventing an API contract. No frontend code touched. No architecture changed. No APIs invented.
2026-07-26 16:10:33 +04:00
see `normalizeWebSession`, and `§4 (Authentication, this document)` §1.3 for the full alias
priority lists). A backend should send a real `expiresAt`/`expires` (else the
frontend fabricates `now + 3600s`).
#### 2.8.2 Marketplace / storefront API (LIVE)
Base: `ApiConfigService.getBaseUrl()` (tenant-resolved; `/api` on localhost,
else `https://{tenant}.api.dexarmarket.ru:445` / `https://api.dexarmarket.ru:445`).
Source: `src/app/services/api.service.ts` (`ApiService`). Headers attached by
`apiHeadersInterceptor`: `X-Region`, `X-Language` (RU/EN/AM), `Currency`
(default RUB), `WebSessionID`. The primary product wire DTO is `Item`
(`src/app/models/item.model.ts`), reconciled by the large tolerant
`normalizeItem()` adapter (BACKEND-AUDIT §7 — treat it as the tolerance
contract). This domain's full per-endpoint catalog belongs to the CRUD-domain
section; summarized here as the framework anchor:
| Endpoint | Method | Auth | Params / Body | Response |
|---|---|---|---|---|
| `/ping` | GET | session headers | — | `{ message }` |
| `/category` | GET | session headers | — | `Category[]` (normalized) |
| `/category/{id}` | GET | session headers | `count`, `skip` | `Item[]` |
| `/items/{id}` | GET | session headers | — | `Item` |
| `/searchitems` | GET | session headers | `search`, `count`, `skip`, `categoryIDs?`, `minPrice?`, `maxPrice?`, `tag?`, `sort?` | `{ items: Item[], total: number }` |
| `/items/randomitems` | GET | session headers | `count`, `category?` | `Item[]` (featured/random) |
| `/websession/{sessionId}` | POST | session headers | item array | cart echo |
| `/items/{id}/callback` | POST | session headers | `{ rating, comment, sessionID, timestamp }` | `{ message }` (review) |
| `/items/{id}/questiion` | POST | session headers | `{ question, sessionID, timestamp }` | `{ message }` (question — **literal typo `questiion` matches backend spec**) |
| `/purchase-email` | POST | session headers | `{ email, phone?, telegramUserId, items[] }` | `{ message }` |
#### 2.8.3 Cart / order / payment API (LIVE)
Order/cart-payment endpoints on the **marketplace base**; QR/card status on the
**payment base** `environment.qrApiUrl` (= `https://qr.vitanova.network/api`).
Source: `src/app/services/api.service.ts`. DTOs are inline in that file.
| Endpoint | Method | Base | Auth | Body | Response |
|---|---|---|---|---|---|
| `/cart` | POST | marketplace | session headers | `CartPaymentRequest` | `QrCreateResponse` |
| `/orders` | POST | marketplace | session headers | `CreateOrderRequest` | `CreateOrderResponse` (fire-and-forget after payment) |
| `/qr` | POST | `qrApiUrl` | headers `authorization-key`, `userid-value` | `QrCreateRequest` | `QrCreateResponse` |
| `/qr/dynamic/{partnerId}/{qrId}` | GET | `qrApiUrl` | — | `QrDynamicStatusResponse` |
| `/card/{partnerId}/{orderId}` | GET | `qrApiUrl` | — | `QrDynamicStatusResponse` |
Const `partnerId` = `web-97ec-9c57-4dde-9037-3a68f7f83750`
(`ApiService.cartPaymentPartnerId`). Key interfaces (copied from
`api.service.ts`):
```ts
export interface CartPaymentRequest {
amount: number;
currency: 'RUB';
siteuserID: string;
siteorderID: string;
redirectUrl: string;
telegramUsername: string;
paymentMethod: 'qr' | 'card';
qrDescription?: string;
customerID?: string;
items: Array<{ itemID: number; price: number; name: string; quantity?: number; delivery?: DeliveryOption[] }>;
}
export interface CreateOrderRequest {
items: Array<{ productId: string; name: string; quantity: number; price: number }>;
customer: { name: string; email: string; phone: string };
payment?: { method: string; currency: string };
shipping?: { address: string; method: string; trackingNumber: string };
}
export interface CreateOrderResponse {
id: string;
orderNumber: string;
status: string;
total: number;
currency: string;
}
export interface QrCreateResponse {
qrId?: string; qrID?: string; nspkID?: string; nspkId?: string;
nspkurl?: string; orderID?: string; url?: string; bankUrl?: string;
status?: string; qrStatus?: string; qrExpirationDate?: string; qrTTL?: number;
payload?: string; Payload?: string; qrUrl?: string;
partnerqrID?: string | number; partnerID?: string | number;
partnerId?: string | number; PartnerID?: string | number;
}
export interface QrDynamicStatusResponse {
additionalInfo: string; paymentPurpose: string; amount: number; code: string;
createDate: string; currency: string; order: string; status: string;
qrId: string; transactionDate: string; transactionId: number; qrExpirationDate: string;
}
```
The `QrCreateResponse` is deliberately alias-tolerant (many casings for id /
url / partner fields) — the frontend resolves the effective id/link via
`resolvePaymentQrId()` / `resolvePaymentLink()` / `resolveBankPaymentUrl()`. A
backend can pick one canonical casing; the frontend will still read it.
Example — create cart payment:
```http
POST https://api.dexarmarket.ru:445/cart
WebSessionID: 3f1c2a0e-…
Content-Type: application/json
{
"amount": 4990,
"currency": "RUB",
"siteuserID": "8823771",
"siteorderID": "order-2026-0007",
"redirectUrl": "https://marketplace.local/checkout/done",
"telegramUsername": "buyer_ivan",
"paymentMethod": "qr",
"items": [{ "itemID": 101, "price": 4990, "name": "Wireless Keyboard", "quantity": 1 }]
}
```
```json
{ "qrId": "QR-77f0", "nspkurl": "https://qr.nspk.ru/AD10…", "status": "created", "qrExpirationDate": "2026-07-26T04:10:00Z" }
```
#### 2.8.4 Widget manifest (LIVE)
Source: `src/app/widgets/registry/widget-manifest.service.ts`. URL comes from
`bootstrap.widgetRegistry.manifestUrl`, falling back to
`/assets/mock/bootstrap/widget-manifest.json`.
| Endpoint | Method | Auth | Response |
|---|---|---|---|
| `<bootstrap.widgetRegistry.manifestUrl>` | GET | none | `WidgetManifestFile` |
On any error it falls back to `of({ widgets: [] })` (never throws to the UI).
Interface (`src/app/widgets/contracts/widget-manifest.contract.ts`):
```ts
export interface WidgetManifestFile {
widgets: WidgetManifestEntry[];
}
export interface WidgetManifestEntry {
type: string;
version: string;
componentKey: string;
supportedLayouts: WidgetLayoutSupport[]; // 'stack'|'grid'|'hero'|'carousel'|'split'
supportedDataSources: WidgetDataSourceName[]; // 'featured'|'latest'|'category'|'manual'|'related'|'future'|'root'|'parent'
settingsSchema: WidgetSettingsSchema; // { type: 'object', properties, required? }
defaultSettings: Record<string, unknown>;
metadataSupport?: WidgetMetadataSupport;
enabled?: boolean;
}
```
Example response:
```json
{
"widgets": [
{
"type": "hero",
"version": "1.0.0",
"componentKey": "HeroWidgetComponent",
"supportedLayouts": ["hero"],
"supportedDataSources": ["manual"],
"settingsSchema": { "type": "object", "properties": { "title": { "type": "string" } } },
"defaultSettings": { "title": "Welcome" },
"enabled": true
}
]
}
```
#### 2.8.5 Regions / location (LIVE)
Source: `src/app/services/location.service.ts`.
| Endpoint | Method | Base | Auth | Response |
|---|---|---|---|---|
| `/regions` | GET | marketplace | session headers | `Region[]` |
On error, falls back **silently** to 6 hardcoded regions (moscow, spb, yerevan,
minsk, almaty, tbilisi) — no user-visible error. The selected region's `id`
feeds the `X-Region` header on subsequent marketplace calls. (An external
geo-IP call `GET http://ip-api.com/json/...` is used for auto-detect — external,
not a platform backend.) Interface (`src/app/models/location.model.ts`):
```ts
export interface Region {
id: string;
city: string;
country: string;
countryCode: string;
timezone?: string;
}
```
Example response:
```json
[
{ "id": "moscow", "city": "Москва", "country": "Россия", "countryCode": "RU", "timezone": "Europe/Moscow" },
{ "id": "yerevan", "city": "Ереван", "country": "Армения", "countryCode": "AM", "timezone": "Asia/Yerevan" }
]
```
docs: fill 2 gaps in BACKEND.md against final handoff checklist Reviewed BACKEND.md top to bottom (4775 lines, 10 sections) against the full backend-handoff checklist (auth, bootstrap, every endpoint, media, all domains, pagination/filter/sort/search, error contract, maintenance mode, status codes, versioning, rate limits, CORS, security, websocket/events, mock-to-api migration). Confirmed already covered, no action: Authentication (§4, all sub-items), Bootstrap (§1, full), every domain's CRUD contract (§3.1- 3.20, includes Moderation under 3.17.b), Media (§7), SEO (bootstrap SeoConfig + per-page seo + sitemap tracked as remaining work), Error Model (§6), Maintenance Mode (§10), Migration guide (§8). Added (genuine gaps, not covered anywhere in the doc): - §2.10 API path versioning - no endpoint has a version segment/header anywhere; only BootstrapConfig.schemaVersion exists and that only versions the bootstrap payload shape, not the API surface. Flagged as a backend/infra decision with zero frontend impact either way. - §2.11 Real-time/WebSocket - confirmed no WebSocket/SSE exists anywhere in the frontend; consolidated the 5 places that look "live" (QR/Telegram login, payment status, session validity, maintenance notice, admin monitoring) into one table, all client-side polling. Flagged push-vs-poll as a backend decision, most relevant to payment latency and the session-revocation propagation delay. - Renumbered the section's "Consolidated requires-backend-decision" list 2.9 -> 2.12 (moved after the two new subsections, no other content changed) and added both new items to it. No other §2.x cross-references existed elsewhere in the doc to update. No duplication found requiring merge; docs/archive/BACKEND_API.md cross-references are intentional (superseded-but-kept historical detail, per the doc's own stated design), not obsolete/duplicate content.
2026-07-26 15:57:21 +04:00
### 2.10 API path versioning
No endpoint the frontend calls includes a version segment (no `/v1/`, no
`Accept-Version`/`Api-Version` header). Every domain path in this document is
relative to `{base}` = `ApiConfigService.getBaseUrl()` (tenant-resolved; see
§1.9) with no version component anywhere in that resolution chain
(`tenant.apiBaseUrl` from bootstrap, or `/api` on localhost — §1.3, §1.9).
The only version field in the entire contract is `BootstrapConfig.schemaVersion`
(§1.4), which versions the **bootstrap payload shape**, not the API surface —
it's checked for presence only, not semantically enforced, and doesn't apply to
any other endpoint.
> **Requires backend decision:** whether the API surface gets a versioning
> scheme at all (URL path segment, header, or none/evergreen-only), and if so
> whether it's introduced from day one or deferred until the first breaking
> change. No frontend code currently assumes or constructs a version segment,
> so either choice is a pure backend/infra decision with no frontend rework
> unless breaking changes are introduced later.
### 2.11 Real-time / WebSocket
No WebSocket, Server-Sent Events, or other push channel exists anywhere in the
frontend. Every case that looks "live" is client-side polling on a plain
`setInterval`/RxJS `interval`, hitting a normal request/response endpoint:
| What | Mechanism | Where |
|---|---|---|
| QR/Telegram login session state | Poll `GET` session-status endpoint until `active`/expired (§4 Mechanism A, `QrLoginEngine`) | Auth |
| Cart QR/card payment status | Poll cart payment-status endpoint, bounded by response `qrTTL` (min 60s) | §3.3 Orders / payments |
| Session/token validity | Re-checked on next request or next refresh-interval tick — no push invalidation. A backend-side revocation isn't observed by an already-open session until the refresh interval elapses (§4.8 Session invalidation) | Auth |
| Maintenance-notice banner (proposed) | Would be a polling `GET /maintenance-notice` endpoint, not a push channel (§10.7) | Maintenance mode |
| Admin monitoring queue/webhook status (proposed, mock-only today) | Would be plain `GET` on facade refresh, no proposal anywhere for push (§3.19) | Monitoring |
> **Requires backend decision:** whether any of the above should become
> push-based (WebSocket/SSE) instead of polling — most relevant to payment
> status (customer-facing latency) and session revocation (security: a
> revoked admin session stays technically usable client-side for up to one
> refresh interval, §4.8). No frontend code exists for a push channel today,
> so adding one is net-new frontend work, not a swap.
### 2.12 Consolidated "Requires backend decision" items (this section)
- Bootstrap: `X-Language`/`Accept-Language` handling on `GET /bootstrap` (§1.1).
- Bootstrap: whether `schemaVersion` is enforced vs presence-only (§1.4).
- Bootstrap: ETag / conditional-request strategy — none exists today (§1.8).
- Bootstrap: the entire draft→publish write path + save-time version/concurrency
check — no write endpoint exists (§1.10).
- Bootstrap: dedicated fatal-bootstrap-failure UX vs silent degradation (§1.11).
- Framework: whether to introduce a success envelope (breaking for all live
callers) — none exists today (§2.1).
- Framework: pagination page-size limits/max; offset vs page-number
standardization; cursor vs offset (nothing implies cursors) (§2.2).
- Framework: reconcile catalog-config sort vocabulary (7) vs search-endpoint
`sort` vocabulary (5); wire encoding for admin list sorting (§2.3).
- Framework: query-string encoding for admin list filters and the `'all'`
sentinel (§2.4).
- Framework: server-side autocomplete/trending search endpoints + min-query
contract (§2.5).
docs: fill 2 gaps in BACKEND.md against final handoff checklist Reviewed BACKEND.md top to bottom (4775 lines, 10 sections) against the full backend-handoff checklist (auth, bootstrap, every endpoint, media, all domains, pagination/filter/sort/search, error contract, maintenance mode, status codes, versioning, rate limits, CORS, security, websocket/events, mock-to-api migration). Confirmed already covered, no action: Authentication (§4, all sub-items), Bootstrap (§1, full), every domain's CRUD contract (§3.1- 3.20, includes Moderation under 3.17.b), Media (§7), SEO (bootstrap SeoConfig + per-page seo + sitemap tracked as remaining work), Error Model (§6), Maintenance Mode (§10), Migration guide (§8). Added (genuine gaps, not covered anywhere in the doc): - §2.10 API path versioning - no endpoint has a version segment/header anywhere; only BootstrapConfig.schemaVersion exists and that only versions the bootstrap payload shape, not the API surface. Flagged as a backend/infra decision with zero frontend impact either way. - §2.11 Real-time/WebSocket - confirmed no WebSocket/SSE exists anywhere in the frontend; consolidated the 5 places that look "live" (QR/Telegram login, payment status, session validity, maintenance notice, admin monitoring) into one table, all client-side polling. Flagged push-vs-poll as a backend decision, most relevant to payment latency and the session-revocation propagation delay. - Renumbered the section's "Consolidated requires-backend-decision" list 2.9 -> 2.12 (moved after the two new subsections, no other content changed) and added both new items to it. No other §2.x cross-references existed elsewhere in the doc to update. No duplication found requiring merge; docs/archive/BACKEND_API.md cross-references are intentional (superseded-but-kept historical detail, per the doc's own stated design), not obsolete/duplicate content.
2026-07-26 15:57:21 +04:00
- Framework: API path/header versioning scheme, if any — none exists today (§2.10).
- Framework: whether payment-status polling and session revocation should
become push-based (WebSocket/SSE) instead of polling (§2.11).
---
## 3. CRUD Contracts
Exhaustive per-domain, per-endpoint contract for every backend touch-point the
Angular frontend expects. Derived from source on branch `B2B` and cross-checked
against `docs/context/BACKEND-AUDIT.md` (the this-session audit — the ground
docs: final project closeout - classify TODO, backend spec, status Classified every TODO.md item into one of DONE/BACKEND/PRODUCT DECISION/FUTURE VERSION/BUG, verified against source, not against prior docs: - BACKEND items (bootstrap content, builder draft/publish, 6 admin CRUD domains, media pipeline) confirmed already covered by BACKEND_INTEGRATION.md; appended a mapping appendix rather than duplicating raw bullets. Fixed 22 stale internal BACKEND_API.md cross-references left over from before that file was archived. - PRODUCT DECISION items (dark mode, brand-color WCAG contrast, stars.component token gap, footer Contacts content, advanced analytics, payment providers) moved to new docs/PRODUCT_BACKLOG.md. - FUTURE VERSION items (Angular 22, bundle splitting, cart-modal composition cleanup, hero-spacing investigation) moved to new docs/FUTURE_FEATURES.md. - BUG: rewrote docs/KNOWN-ISSUES.md down to the one real, verified, currently-reproducible frontend bug (Ed25519 admin-auth error codes session-expired/invalid-signature are unreachable - toAuthErrorShape() never reads a body error code, only maps HTTP status, and no status ever produces those two codes - confirmed by reading auth.service.ts + auth-error.model.ts). Condensed the "Fixed" history instead of carrying full verbose repro text forward. - DONE items removed outright (dead-code deletion, dashboard false positive, RC-02 fixes, stale "dynamic-renderer unwired"/"178 missing keys" claims already disproven by source). docs/TODO.md rewritten to the exact "no blockers" template - nothing left qualifies as a release blocker. New docs/PROJECT_STATUS.md: honest per-area status (frontend/backend/ docs/auth/builder/storefront/admin), known limitations, and explicit production/backend/demo readiness calls - including correcting an initial draft's unpushed-commit count (53, not 10, per git log origin/B2B..HEAD). New docs/NEXT_PHASE.md: work that can only start once a real backend exists (gateway swap-in, mock removal, dormant-auth activation, role enforcement, integration/E2E tests, perf profiling, monitoring, maintenance-mode UI). docs/PROJECT_INDEX.md (the stated entry point) updated to link the new doc set and stop pointing at the now-archived BACKEND_API.md/AUTH.md. docs/FRONTEND-ROADMAP.md's "Known open items" replaced with pointers to the new category-split docs instead of a duplicated mixed list. Not swept: a handful of low-traffic docs (architecture ADRs, FRONTEND.md, EDITOR.md, ARCHITECTURE.md, PROJECT-STRUCTURE.md, StaticPages.md, ADMIN.md) still reference the old BACKEND_API.md/ AUTH.md filenames - noted as a known gap in PROJECT_STATUS.md rather than touched blindly, since they're historical-context docs, not the navigation entry point.
2026-07-26 12:35:26 +04:00
truth for what code actually does), the prior `docs/archive/BACKEND_API.md`, and the two
sibling specs written this session:
docs: finalize BACKEND.md as a self-contained implementation prompt Goal: this document alone, pasted into a fresh Claude session, should be enough for a backend dev to implement against without getting stuck or inventing conventions the frontend doesn't actually need. Fixed a real bug: ~65 cross-references throughout the document pointed to docs/AUTHENTICATION.md, docs/ERROR_CONTRACT.md, docs/MAINTENANCE_MODE.md - three sibling docs that were deleted and fully merged into this document's own §4/§6/§10 during the earlier doc-consolidation pass, but the in-text references were never updated. A fresh agent following those links would hit dead ends repeatedly. Bulk-replaced with in- document section references; hand-fixed ~4 sentences that framed §6/ §10 as "sibling task, in progress" (stale - both are complete, this document's §6/§10 already are the settled contract, nothing to wait on). Added "Recommended default" to every item across the document's 5 consolidated "Requires backend decision" registers (§2.12 framework, §3.21 CRUD cross-cutting - 18 items, §4 §12 auth open items - 9 items, §6 error-model summary - 9 items, §10 maintenance-mode list - 5 items). Each default is derived from what the frontend already implies or standard REST/security convention - no invented APIs or business rules. ~6 items are explicitly flagged as real business/security decisions instead (order state-machine rules, Ed25519 cutover strategy, refresh-token reuse-detection posture) since those carry consequences no amount of frontend-code-reading can resolve. Added a "How to use this document" preamble up front: work in §9's dependency order not document order, apply recommended defaults and keep moving, only stop for the explicitly-flagged business/security items, don't invent beyond what's written or directly implied. Frontend side: confirmed nothing else is left. docs/TODO.md already has zero blockers; docs/KNOWN-ISSUES.md's one open item (unreachable Ed25519 error-code UI) is correctly left open and documented rather than faked closed - fixing it needs a real backend emitting real distinguishable error codes, which doesn't exist yet and can't be fabricated without inventing an API contract. No frontend code touched. No architecture changed. No APIs invented.
2026-07-26 16:10:33 +04:00
- **Auth / headers / JWT** — see `§4 (Authentication, this document)`. This section never
re-defines the auth header format; it references it.
- **Error response envelope + per-status semantics** — see
docs: finalize BACKEND.md as a self-contained implementation prompt Goal: this document alone, pasted into a fresh Claude session, should be enough for a backend dev to implement against without getting stuck or inventing conventions the frontend doesn't actually need. Fixed a real bug: ~65 cross-references throughout the document pointed to docs/AUTHENTICATION.md, docs/ERROR_CONTRACT.md, docs/MAINTENANCE_MODE.md - three sibling docs that were deleted and fully merged into this document's own §4/§6/§10 during the earlier doc-consolidation pass, but the in-text references were never updated. A fresh agent following those links would hit dead ends repeatedly. Bulk-replaced with in- document section references; hand-fixed ~4 sentences that framed §6/ §10 as "sibling task, in progress" (stale - both are complete, this document's §6/§10 already are the settled contract, nothing to wait on). Added "Recommended default" to every item across the document's 5 consolidated "Requires backend decision" registers (§2.12 framework, §3.21 CRUD cross-cutting - 18 items, §4 §12 auth open items - 9 items, §6 error-model summary - 9 items, §10 maintenance-mode list - 5 items). Each default is derived from what the frontend already implies or standard REST/security convention - no invented APIs or business rules. ~6 items are explicitly flagged as real business/security decisions instead (order state-machine rules, Ed25519 cutover strategy, refresh-token reuse-detection posture) since those carry consequences no amount of frontend-code-reading can resolve. Added a "How to use this document" preamble up front: work in §9's dependency order not document order, apply recommended defaults and keep moving, only stop for the explicitly-flagged business/security items, don't invent beyond what's written or directly implied. Frontend side: confirmed nothing else is left. docs/TODO.md already has zero blockers; docs/KNOWN-ISSUES.md's one open item (unreachable Ed25519 error-code UI) is correctly left open and documented rather than faked closed - fixing it needs a real backend emitting real distinguishable error codes, which doesn't exist yet and can't be fabricated without inventing an API contract. No frontend code touched. No architecture changed. No APIs invented.
2026-07-26 16:10:33 +04:00
`§6 (Error Model, this document)`. This section names *which* statuses apply per
endpoint and *why*, but the wire shape of the error body is owned by that doc.
### 3.0 Conventions used in this section
**Two API base URLs are in play** (`BACKEND-AUDIT.md` §2):
- **Marketplace / tenant API** — `ApiConfigService.getBaseUrl()`
(`src/app/core/config/api-config.service.ts`). Localhost → `/api`; otherwise a
per-tenant origin (default `https://api.dexarmarket.ru:445`). Tenant isolation
is **by subdomain/base-URL only** — no `X-Tenant` header, no `/tenant/{id}`
docs: finalize BACKEND.md as a self-contained implementation prompt Goal: this document alone, pasted into a fresh Claude session, should be enough for a backend dev to implement against without getting stuck or inventing conventions the frontend doesn't actually need. Fixed a real bug: ~65 cross-references throughout the document pointed to docs/AUTHENTICATION.md, docs/ERROR_CONTRACT.md, docs/MAINTENANCE_MODE.md - three sibling docs that were deleted and fully merged into this document's own §4/§6/§10 during the earlier doc-consolidation pass, but the in-text references were never updated. A fresh agent following those links would hit dead ends repeatedly. Bulk-replaced with in- document section references; hand-fixed ~4 sentences that framed §6/ §10 as "sibling task, in progress" (stale - both are complete, this document's §6/§10 already are the settled contract, nothing to wait on). Added "Recommended default" to every item across the document's 5 consolidated "Requires backend decision" registers (§2.12 framework, §3.21 CRUD cross-cutting - 18 items, §4 §12 auth open items - 9 items, §6 error-model summary - 9 items, §10 maintenance-mode list - 5 items). Each default is derived from what the frontend already implies or standard REST/security convention - no invented APIs or business rules. ~6 items are explicitly flagged as real business/security decisions instead (order state-machine rules, Ed25519 cutover strategy, refresh-token reuse-detection posture) since those carry consequences no amount of frontend-code-reading can resolve. Added a "How to use this document" preamble up front: work in §9's dependency order not document order, apply recommended defaults and keep moving, only stop for the explicitly-flagged business/security items, don't invent beyond what's written or directly implied. Frontend side: confirmed nothing else is left. docs/TODO.md already has zero blockers; docs/KNOWN-ISSUES.md's one open item (unreachable Ed25519 error-code UI) is correctly left open and documented rather than faked closed - fixing it needs a real backend emitting real distinguishable error codes, which doesn't exist yet and can't be fabricated without inventing an API contract. No frontend code touched. No architecture changed. No APIs invented.
2026-07-26 16:10:33 +04:00
prefix (`§4` §10). All storefront reads and all admin CRUD go
here.
- **Payment / QR API** — `environment.qrApiUrl` (`https://qr.vitanova.network/api`).
Only cart/order payment + status polling (§3.3).
- **Session-auth API** — `environment.authApiUrl`. Login/session only, covered by
docs: finalize BACKEND.md as a self-contained implementation prompt Goal: this document alone, pasted into a fresh Claude session, should be enough for a backend dev to implement against without getting stuck or inventing conventions the frontend doesn't actually need. Fixed a real bug: ~65 cross-references throughout the document pointed to docs/AUTHENTICATION.md, docs/ERROR_CONTRACT.md, docs/MAINTENANCE_MODE.md - three sibling docs that were deleted and fully merged into this document's own §4/§6/§10 during the earlier doc-consolidation pass, but the in-text references were never updated. A fresh agent following those links would hit dead ends repeatedly. Bulk-replaced with in- document section references; hand-fixed ~4 sentences that framed §6/ §10 as "sibling task, in progress" (stale - both are complete, this document's §6/§10 already are the settled contract, nothing to wait on). Added "Recommended default" to every item across the document's 5 consolidated "Requires backend decision" registers (§2.12 framework, §3.21 CRUD cross-cutting - 18 items, §4 §12 auth open items - 9 items, §6 error-model summary - 9 items, §10 maintenance-mode list - 5 items). Each default is derived from what the frontend already implies or standard REST/security convention - no invented APIs or business rules. ~6 items are explicitly flagged as real business/security decisions instead (order state-machine rules, Ed25519 cutover strategy, refresh-token reuse-detection posture) since those carry consequences no amount of frontend-code-reading can resolve. Added a "How to use this document" preamble up front: work in §9's dependency order not document order, apply recommended defaults and keep moving, only stop for the explicitly-flagged business/security items, don't invent beyond what's written or directly implied. Frontend side: confirmed nothing else is left. docs/TODO.md already has zero blockers; docs/KNOWN-ISSUES.md's one open item (unreachable Ed25519 error-code UI) is correctly left open and documented rather than faked closed - fixing it needs a real backend emitting real distinguishable error codes, which doesn't exist yet and can't be fabricated without inventing an API contract. No frontend code touched. No architecture changed. No APIs invented.
2026-07-26 16:10:33 +04:00
`§4 (Authentication, this document)`, not repeated here.
**LIVE vs PROPOSED paths.** Only the storefront catalog/cart/engagement reads and
the single `AdminCategoriesApiGateway` have **literal HTTP paths in code**. Every
other admin CRUD path below is **PROPOSED** — no URL literal exists anywhere in
the frontend for it (`BACKEND-AUDIT.md` §24: "No literal `/admin/*`, `/builder/*`,
or per-admin-domain backoffice CRUD paths exist in code"). The admin gateways are
in-memory `*LocalGateway` classes that never construct a URL. The proposals here
follow the **one real precedent** in the codebase — `AdminCategoriesApiGateway`
(`src/app/features/admin/categories/services/admin-categories-api.gateway.ts`),
which uses:
```
GET {base}/backoffice/categories ?search&visibility&includeDeleted
GET {base}/backoffice/categories/{id}
POST {base}/backoffice/categories (body = category minus server-owned fields)
PUT {base}/backoffice/categories/{id}
DELETE {base}/backoffice/categories/{id} (soft delete)
POST {base}/backoffice/categories/{id}/restore
GET {base}/backoffice/categories/slug-taken ?slug&excludingId
```
All other admin paths below are proposed by analogy to this shape (`{base}/backoffice/<domain>`).
Builder/config domains (nav, footer, branding, languages, CMS, homepage, widgets)
are proposed under `{base}/builder/...` because the bootstrap document declares a
`apiEndpoints.builder` record for exactly this, but **no builder write call exists
in code today** — the entire builder/CMS surface is LOCAL-ONLY (in-memory
bootstrap + localStorage drafts), so those endpoints are marked **PROPOSED /
FUTURE** throughout.
**Headers (all requests).** Marketplace API requests carry `X-Region`,
`X-Language` (`RU|EN|AM`), `Currency` (default `RUB`), and `WebSessionID`
(`apiHeadersInterceptor`, `BACKEND-AUDIT.md` §3). Requests whose URL contains
`/admin/`, `/backoffice/`, `/builder/`, or `/media/` **additionally** get
`AdminWebSessionID` and, if an admin token is stored, `Authorization: Bearer
<token>` (`adminAuthHeadersInterceptor`). These header names/values are owned by
docs: finalize BACKEND.md as a self-contained implementation prompt Goal: this document alone, pasted into a fresh Claude session, should be enough for a backend dev to implement against without getting stuck or inventing conventions the frontend doesn't actually need. Fixed a real bug: ~65 cross-references throughout the document pointed to docs/AUTHENTICATION.md, docs/ERROR_CONTRACT.md, docs/MAINTENANCE_MODE.md - three sibling docs that were deleted and fully merged into this document's own §4/§6/§10 during the earlier doc-consolidation pass, but the in-text references were never updated. A fresh agent following those links would hit dead ends repeatedly. Bulk-replaced with in- document section references; hand-fixed ~4 sentences that framed §6/ §10 as "sibling task, in progress" (stale - both are complete, this document's §6/§10 already are the settled contract, nothing to wait on). Added "Recommended default" to every item across the document's 5 consolidated "Requires backend decision" registers (§2.12 framework, §3.21 CRUD cross-cutting - 18 items, §4 §12 auth open items - 9 items, §6 error-model summary - 9 items, §10 maintenance-mode list - 5 items). Each default is derived from what the frontend already implies or standard REST/security convention - no invented APIs or business rules. ~6 items are explicitly flagged as real business/security decisions instead (order state-machine rules, Ed25519 cutover strategy, refresh-token reuse-detection posture) since those carry consequences no amount of frontend-code-reading can resolve. Added a "How to use this document" preamble up front: work in §9's dependency order not document order, apply recommended defaults and keep moving, only stop for the explicitly-flagged business/security items, don't invent beyond what's written or directly implied. Frontend side: confirmed nothing else is left. docs/TODO.md already has zero blockers; docs/KNOWN-ISSUES.md's one open item (unreachable Ed25519 error-code UI) is correctly left open and documented rather than faked closed - fixing it needs a real backend emitting real distinguishable error codes, which doesn't exist yet and can't be fabricated without inventing an API contract. No frontend code touched. No architecture changed. No APIs invented.
2026-07-26 16:10:33 +04:00
`§4` — assume them on every admin endpoint below unless stated.
**JWT requirement.** No endpoint in the app requires a verified JWT **today**.
Mechanism A (Telegram session) authenticates admins with an opaque
`AdminWebSessionID`, not a JWT; the Ed25519 JWT flow (Mechanism B) is fully wired
docs: finalize BACKEND.md as a self-contained implementation prompt Goal: this document alone, pasted into a fresh Claude session, should be enough for a backend dev to implement against without getting stuck or inventing conventions the frontend doesn't actually need. Fixed a real bug: ~65 cross-references throughout the document pointed to docs/AUTHENTICATION.md, docs/ERROR_CONTRACT.md, docs/MAINTENANCE_MODE.md - three sibling docs that were deleted and fully merged into this document's own §4/§6/§10 during the earlier doc-consolidation pass, but the in-text references were never updated. A fresh agent following those links would hit dead ends repeatedly. Bulk-replaced with in- document section references; hand-fixed ~4 sentences that framed §6/ §10 as "sibling task, in progress" (stale - both are complete, this document's §6/§10 already are the settled contract, nothing to wait on). Added "Recommended default" to every item across the document's 5 consolidated "Requires backend decision" registers (§2.12 framework, §3.21 CRUD cross-cutting - 18 items, §4 §12 auth open items - 9 items, §6 error-model summary - 9 items, §10 maintenance-mode list - 5 items). Each default is derived from what the frontend already implies or standard REST/security convention - no invented APIs or business rules. ~6 items are explicitly flagged as real business/security decisions instead (order state-machine rules, Ed25519 cutover strategy, refresh-token reuse-detection posture) since those carry consequences no amount of frontend-code-reading can resolve. Added a "How to use this document" preamble up front: work in §9's dependency order not document order, apply recommended defaults and keep moving, only stop for the explicitly-flagged business/security items, don't invent beyond what's written or directly implied. Frontend side: confirmed nothing else is left. docs/TODO.md already has zero blockers; docs/KNOWN-ISSUES.md's one open item (unreachable Ed25519 error-code UI) is correctly left open and documented rather than faked closed - fixing it needs a real backend emitting real distinguishable error codes, which doesn't exist yet and can't be fabricated without inventing an API contract. No frontend code touched. No architecture changed. No APIs invented.
2026-07-26 16:10:33 +04:00
but dormant (`§4` §2). For every admin endpoint below, "JWT
requirement" is therefore stated as: **Today: `AdminWebSessionID` session header
(Mechanism A). Target: `Authorization: Bearer <Ed25519 JWT>` once Mechanism B is
cut over.** Server-side authorization must be enforced regardless of any
docs: finalize BACKEND.md as a self-contained implementation prompt Goal: this document alone, pasted into a fresh Claude session, should be enough for a backend dev to implement against without getting stuck or inventing conventions the frontend doesn't actually need. Fixed a real bug: ~65 cross-references throughout the document pointed to docs/AUTHENTICATION.md, docs/ERROR_CONTRACT.md, docs/MAINTENANCE_MODE.md - three sibling docs that were deleted and fully merged into this document's own §4/§6/§10 during the earlier doc-consolidation pass, but the in-text references were never updated. A fresh agent following those links would hit dead ends repeatedly. Bulk-replaced with in- document section references; hand-fixed ~4 sentences that framed §6/ §10 as "sibling task, in progress" (stale - both are complete, this document's §6/§10 already are the settled contract, nothing to wait on). Added "Recommended default" to every item across the document's 5 consolidated "Requires backend decision" registers (§2.12 framework, §3.21 CRUD cross-cutting - 18 items, §4 §12 auth open items - 9 items, §6 error-model summary - 9 items, §10 maintenance-mode list - 5 items). Each default is derived from what the frontend already implies or standard REST/security convention - no invented APIs or business rules. ~6 items are explicitly flagged as real business/security decisions instead (order state-machine rules, Ed25519 cutover strategy, refresh-token reuse-detection posture) since those carry consequences no amount of frontend-code-reading can resolve. Added a "How to use this document" preamble up front: work in §9's dependency order not document order, apply recommended defaults and keep moving, only stop for the explicitly-flagged business/security items, don't invent beyond what's written or directly implied. Frontend side: confirmed nothing else is left. docs/TODO.md already has zero blockers; docs/KNOWN-ISSUES.md's one open item (unreachable Ed25519 error-code UI) is correctly left open and documented rather than faked closed - fixing it needs a real backend emitting real distinguishable error codes, which doesn't exist yet and can't be fabricated without inventing an API contract. No frontend code touched. No architecture changed. No APIs invented.
2026-07-26 16:10:33 +04:00
client-side guard (`§4` §11.6).
**Required permission / role.** The frontend's only permission model is the
docs: finalize BACKEND.md as a self-contained implementation prompt Goal: this document alone, pasted into a fresh Claude session, should be enough for a backend dev to implement against without getting stuck or inventing conventions the frontend doesn't actually need. Fixed a real bug: ~65 cross-references throughout the document pointed to docs/AUTHENTICATION.md, docs/ERROR_CONTRACT.md, docs/MAINTENANCE_MODE.md - three sibling docs that were deleted and fully merged into this document's own §4/§6/§10 during the earlier doc-consolidation pass, but the in-text references were never updated. A fresh agent following those links would hit dead ends repeatedly. Bulk-replaced with in- document section references; hand-fixed ~4 sentences that framed §6/ §10 as "sibling task, in progress" (stale - both are complete, this document's §6/§10 already are the settled contract, nothing to wait on). Added "Recommended default" to every item across the document's 5 consolidated "Requires backend decision" registers (§2.12 framework, §3.21 CRUD cross-cutting - 18 items, §4 §12 auth open items - 9 items, §6 error-model summary - 9 items, §10 maintenance-mode list - 5 items). Each default is derived from what the frontend already implies or standard REST/security convention - no invented APIs or business rules. ~6 items are explicitly flagged as real business/security decisions instead (order state-machine rules, Ed25519 cutover strategy, refresh-token reuse-detection posture) since those carry consequences no amount of frontend-code-reading can resolve. Added a "How to use this document" preamble up front: work in §9's dependency order not document order, apply recommended defaults and keep moving, only stop for the explicitly-flagged business/security items, don't invent beyond what's written or directly implied. Frontend side: confirmed nothing else is left. docs/TODO.md already has zero blockers; docs/KNOWN-ISSUES.md's one open item (unreachable Ed25519 error-code UI) is correctly left open and documented rather than faked closed - fixing it needs a real backend emitting real distinguishable error codes, which doesn't exist yet and can't be fabricated without inventing an API contract. No frontend code touched. No architecture changed. No APIs invented.
2026-07-26 16:10:33 +04:00
coarse `ROLE_PERMISSIONS` table (`§4` §9.1): `backoffice.read`,
`backoffice.write`, `builder.read`, `builder.write`, `users.manage`,
`settings.manage`. The frontend does **not** enforce per-domain permissions on
admin CRUD today (the live `adminAuthGuard` only checks "is there an admin
session at all"). Where a specific permission is the obvious fit it is named as
**Proposed**; any finer per-role assignment is **Requires backend decision**
docs: finalize BACKEND.md as a self-contained implementation prompt Goal: this document alone, pasted into a fresh Claude session, should be enough for a backend dev to implement against without getting stuck or inventing conventions the frontend doesn't actually need. Fixed a real bug: ~65 cross-references throughout the document pointed to docs/AUTHENTICATION.md, docs/ERROR_CONTRACT.md, docs/MAINTENANCE_MODE.md - three sibling docs that were deleted and fully merged into this document's own §4/§6/§10 during the earlier doc-consolidation pass, but the in-text references were never updated. A fresh agent following those links would hit dead ends repeatedly. Bulk-replaced with in- document section references; hand-fixed ~4 sentences that framed §6/ §10 as "sibling task, in progress" (stale - both are complete, this document's §6/§10 already are the settled contract, nothing to wait on). Added "Recommended default" to every item across the document's 5 consolidated "Requires backend decision" registers (§2.12 framework, §3.21 CRUD cross-cutting - 18 items, §4 §12 auth open items - 9 items, §6 error-model summary - 9 items, §10 maintenance-mode list - 5 items). Each default is derived from what the frontend already implies or standard REST/security convention - no invented APIs or business rules. ~6 items are explicitly flagged as real business/security decisions instead (order state-machine rules, Ed25519 cutover strategy, refresh-token reuse-detection posture) since those carry consequences no amount of frontend-code-reading can resolve. Added a "How to use this document" preamble up front: work in §9's dependency order not document order, apply recommended defaults and keep moving, only stop for the explicitly-flagged business/security items, don't invent beyond what's written or directly implied. Frontend side: confirmed nothing else is left. docs/TODO.md already has zero blockers; docs/KNOWN-ISSUES.md's one open item (unreachable Ed25519 error-code UI) is correctly left open and documented rather than faked closed - fixing it needs a real backend emitting real distinguishable error codes, which doesn't exist yet and can't be fabricated without inventing an API contract. No frontend code touched. No architecture changed. No APIs invented.
2026-07-26 16:10:33 +04:00
(`§4` §9.1 already flags fine-grained permissions as out of scope).
**Error responses.** Unless a domain-specific note says otherwise, every endpoint
docs: finalize BACKEND.md as a self-contained implementation prompt Goal: this document alone, pasted into a fresh Claude session, should be enough for a backend dev to implement against without getting stuck or inventing conventions the frontend doesn't actually need. Fixed a real bug: ~65 cross-references throughout the document pointed to docs/AUTHENTICATION.md, docs/ERROR_CONTRACT.md, docs/MAINTENANCE_MODE.md - three sibling docs that were deleted and fully merged into this document's own §4/§6/§10 during the earlier doc-consolidation pass, but the in-text references were never updated. A fresh agent following those links would hit dead ends repeatedly. Bulk-replaced with in- document section references; hand-fixed ~4 sentences that framed §6/ §10 as "sibling task, in progress" (stale - both are complete, this document's §6/§10 already are the settled contract, nothing to wait on). Added "Recommended default" to every item across the document's 5 consolidated "Requires backend decision" registers (§2.12 framework, §3.21 CRUD cross-cutting - 18 items, §4 §12 auth open items - 9 items, §6 error-model summary - 9 items, §10 maintenance-mode list - 5 items). Each default is derived from what the frontend already implies or standard REST/security convention - no invented APIs or business rules. ~6 items are explicitly flagged as real business/security decisions instead (order state-machine rules, Ed25519 cutover strategy, refresh-token reuse-detection posture) since those carry consequences no amount of frontend-code-reading can resolve. Added a "How to use this document" preamble up front: work in §9's dependency order not document order, apply recommended defaults and keep moving, only stop for the explicitly-flagged business/security items, don't invent beyond what's written or directly implied. Frontend side: confirmed nothing else is left. docs/TODO.md already has zero blockers; docs/KNOWN-ISSUES.md's one open item (unreachable Ed25519 error-code UI) is correctly left open and documented rather than faked closed - fixing it needs a real backend emitting real distinguishable error codes, which doesn't exist yet and can't be fabricated without inventing an API contract. No frontend code touched. No architecture changed. No APIs invented.
2026-07-26 16:10:33 +04:00
below can return the envelope from `§6` with these statuses:
`401 UNAUTHENTICATED` (no/expired session), `403 FORBIDDEN` (wrong role/tenant),
`500 INTERNAL_ERROR`, `503 SERVICE_UNAVAILABLE`/`MAINTENANCE_MODE`. Per-endpoint
notes below add `404`, `409`, `422`, `429` only where they are meaningful. **The
docs: finalize BACKEND.md as a self-contained implementation prompt Goal: this document alone, pasted into a fresh Claude session, should be enough for a backend dev to implement against without getting stuck or inventing conventions the frontend doesn't actually need. Fixed a real bug: ~65 cross-references throughout the document pointed to docs/AUTHENTICATION.md, docs/ERROR_CONTRACT.md, docs/MAINTENANCE_MODE.md - three sibling docs that were deleted and fully merged into this document's own §4/§6/§10 during the earlier doc-consolidation pass, but the in-text references were never updated. A fresh agent following those links would hit dead ends repeatedly. Bulk-replaced with in- document section references; hand-fixed ~4 sentences that framed §6/ §10 as "sibling task, in progress" (stale - both are complete, this document's §6/§10 already are the settled contract, nothing to wait on). Added "Recommended default" to every item across the document's 5 consolidated "Requires backend decision" registers (§2.12 framework, §3.21 CRUD cross-cutting - 18 items, §4 §12 auth open items - 9 items, §6 error-model summary - 9 items, §10 maintenance-mode list - 5 items). Each default is derived from what the frontend already implies or standard REST/security convention - no invented APIs or business rules. ~6 items are explicitly flagged as real business/security decisions instead (order state-machine rules, Ed25519 cutover strategy, refresh-token reuse-detection posture) since those carry consequences no amount of frontend-code-reading can resolve. Added a "How to use this document" preamble up front: work in §9's dependency order not document order, apply recommended defaults and keep moving, only stop for the explicitly-flagged business/security items, don't invent beyond what's written or directly implied. Frontend side: confirmed nothing else is left. docs/TODO.md already has zero blockers; docs/KNOWN-ISSUES.md's one open item (unreachable Ed25519 error-code UI) is correctly left open and documented rather than faked closed - fixing it needs a real backend emitting real distinguishable error codes, which doesn't exist yet and can't be fabricated without inventing an API contract. No frontend code touched. No architecture changed. No APIs invented.
2026-07-26 16:10:33 +04:00
frontend does not parse the error body today** (`§6` "Finding") —
admin list pages collapse every non-2xx into one generic "retry" state
docs: finalize BACKEND.md as a self-contained implementation prompt Goal: this document alone, pasted into a fresh Claude session, should be enough for a backend dev to implement against without getting stuck or inventing conventions the frontend doesn't actually need. Fixed a real bug: ~65 cross-references throughout the document pointed to docs/AUTHENTICATION.md, docs/ERROR_CONTRACT.md, docs/MAINTENANCE_MODE.md - three sibling docs that were deleted and fully merged into this document's own §4/§6/§10 during the earlier doc-consolidation pass, but the in-text references were never updated. A fresh agent following those links would hit dead ends repeatedly. Bulk-replaced with in- document section references; hand-fixed ~4 sentences that framed §6/ §10 as "sibling task, in progress" (stale - both are complete, this document's §6/§10 already are the settled contract, nothing to wait on). Added "Recommended default" to every item across the document's 5 consolidated "Requires backend decision" registers (§2.12 framework, §3.21 CRUD cross-cutting - 18 items, §4 §12 auth open items - 9 items, §6 error-model summary - 9 items, §10 maintenance-mode list - 5 items). Each default is derived from what the frontend already implies or standard REST/security convention - no invented APIs or business rules. ~6 items are explicitly flagged as real business/security decisions instead (order state-machine rules, Ed25519 cutover strategy, refresh-token reuse-detection posture) since those carry consequences no amount of frontend-code-reading can resolve. Added a "How to use this document" preamble up front: work in §9's dependency order not document order, apply recommended defaults and keep moving, only stop for the explicitly-flagged business/security items, don't invent beyond what's written or directly implied. Frontend side: confirmed nothing else is left. docs/TODO.md already has zero blockers; docs/KNOWN-ISSUES.md's one open item (unreachable Ed25519 error-code UI) is correctly left open and documented rather than faked closed - fixing it needs a real backend emitting real distinguishable error codes, which doesn't exist yet and can't be fabricated without inventing an API contract. No frontend code touched. No architecture changed. No APIs invented.
2026-07-26 16:10:33 +04:00
(`§6` "Generic list-page error UI"). So all per-status handling
below is the *contract the backend should honor*, not behavior the current UI
distinguishes.
**Validation rules.** Admin create/edit forms
(`admin-product-form.component.ts`, `admin-category-form.component.ts`) use **no
Angular reactive `Validators`** — confirmed by source search (zero
`Validators.*` matches). Form state is built by factories
(`admin-products-form.factory.ts`, `admin-categories-form.factory.ts`) that seed
empty defaults (e.g. `slug: ''`). Therefore **the frontend enforces essentially
no field validation** on admin CRUD; every validation rule below beyond "the
field exists in the model / is non-nullable in TS" is **Requires backend
decision**. The one real client-side validation convention lives in the builder
(`ProjectValidator``ProjectEditorFacade.fieldError()`), which is client-only
and localStorage-scoped, not backend-fed.
**Pagination.** The admin list models that paginate use a uniform shape:
`filters { …, page: number, pageSize: number }` in, `{ items[], total, page,
pageSize }` out (products, orders, transactions, reviews). Categories, users,
monitoring, customers, analytics return **plain arrays with no pagination**
noted per domain.
---
### 3.1 Products
Two distinct surfaces: **storefront catalog read** (LIVE) and **admin CRUD**
(MOCK-ONLY, no seam — `BACKEND-AUDIT.md` §14).
#### 3.1.a Storefront catalog read — LIVE
Backed by `ApiService` (`src/app/services/api.service.ts`) via
`ApiProductDataProvider``PRODUCT_DATA_PROVIDER``ProductFacade`. The wire
DTO is `Item` (`src/app/models/item.model.ts`); `ApiService.normalizeItem()` is
the tolerance contract (the single largest inline mapper — a backend engineer
should treat it as authoritative for accepted field name variants; see
`BACKEND-AUDIT.md` §7).
| # | Method | Path (LIVE literal) | Purpose | Success |
|---|---|---|---|---|
| P1 | GET | `{base}/items/{itemID}` | Single product | `200` single `Item` (raw wire shape) |
| P2 | GET | `{base}/category/{categoryID}?count&skip` | Products in a category | `200` `Item[]` |
| P3 | GET | `{base}/searchitems?search&count&skip[&categoryIDs&minPrice&maxPrice&tag&sort]` | Search | `200` `{ items: Item[], total: number }` |
| P4 | GET | `{base}/items/randomitems?count[&category]` | Featured/random | `200` `Item[]` |
- **Headers/JWT:** marketplace headers (§3.0). No JWT; anonymous `WebSessionID`
is sufficient. Public read.
- **Query params (P2/P3/P4):** `count` (default 50), `skip` (default 0, offset
pagination). P3 search: `search` (string, required), `categoryIDs`
(comma-joined numeric ids), `minPrice`, `maxPrice` (numbers), `tag` (string),
`sort``relevance | price_asc | price_desc | popular | rating`. Signature:
`ApiService.searchItems()` lines 551-590.
- **Pagination/sorting/filtering:** offset-based (`count`/`skip`); sort + price
+ category + tag filters only on P3. P2 has no sort/filter params. Response P3
is the only one returning a `total` for page math.
- **Error responses:** `404` when `itemID` doesn't exist (P1) — but note the
frontend does **not** distinguish 404 from any other error today
docs: finalize BACKEND.md as a self-contained implementation prompt Goal: this document alone, pasted into a fresh Claude session, should be enough for a backend dev to implement against without getting stuck or inventing conventions the frontend doesn't actually need. Fixed a real bug: ~65 cross-references throughout the document pointed to docs/AUTHENTICATION.md, docs/ERROR_CONTRACT.md, docs/MAINTENANCE_MODE.md - three sibling docs that were deleted and fully merged into this document's own §4/§6/§10 during the earlier doc-consolidation pass, but the in-text references were never updated. A fresh agent following those links would hit dead ends repeatedly. Bulk-replaced with in- document section references; hand-fixed ~4 sentences that framed §6/ §10 as "sibling task, in progress" (stale - both are complete, this document's §6/§10 already are the settled contract, nothing to wait on). Added "Recommended default" to every item across the document's 5 consolidated "Requires backend decision" registers (§2.12 framework, §3.21 CRUD cross-cutting - 18 items, §4 §12 auth open items - 9 items, §6 error-model summary - 9 items, §10 maintenance-mode list - 5 items). Each default is derived from what the frontend already implies or standard REST/security convention - no invented APIs or business rules. ~6 items are explicitly flagged as real business/security decisions instead (order state-machine rules, Ed25519 cutover strategy, refresh-token reuse-detection posture) since those carry consequences no amount of frontend-code-reading can resolve. Added a "How to use this document" preamble up front: work in §9's dependency order not document order, apply recommended defaults and keep moving, only stop for the explicitly-flagged business/security items, don't invent beyond what's written or directly implied. Frontend side: confirmed nothing else is left. docs/TODO.md already has zero blockers; docs/KNOWN-ISSUES.md's one open item (unreachable Ed25519 error-code UI) is correctly left open and documented rather than faked closed - fixing it needs a real backend emitting real distinguishable error codes, which doesn't exist yet and can't be fabricated without inventing an API contract. No frontend code touched. No architecture changed. No APIs invented.
2026-07-26 16:10:33 +04:00
(`§6` §404); a deleted product and a 500 render the same generic
empty-state. `500/503` generic. Calls retry x2 with exponential backoff
(`ApiService.retryConfig`).
- **Validation:** none (read).
- **Example P3 request:** `GET {base}/searchitems?search=phone&count=24&skip=0&categoryIDs=3,7&minPrice=1000&sort=price_asc`
- **Example P3 response:**
```json
{ "items": [ { "id": "1024", "name": "Phone X", "price": 62560, "currency": "RUB", "imgs": ["https://…/x.webp"], "remaining": 12 } ], "total": 87 }
```
**`Item` variants/options (part of P1 payload, not separate endpoints).**
`normalizeItem` accepts `variantOptions[]` (`{ key, label, labels?, options: [{
value, label?, labels?, available? }] }`) and `specificationGroups[]`. The admin
side (§3.1.b) models variants as a flat priced list (`AdminProductVariant`). There
is **no dedicated variants/options CRUD endpoint** — variants are created/updated
as part of the product create/update body. Flagged: variant shape differs between
storefront (`variantOptions` grouped) and admin (`variants` flat priced rows) —
reconciliation is **Requires backend decision** (adapter lives in
`admin-product-form.factory.ts variantsToBackendRows/variantsFromBackendRows`).
#### 3.1.b Admin products CRUD — PROPOSED (MOCK-ONLY today, no DI seam)
Contract: `AdminProductsGateway`
(`src/app/features/admin/products/services/admin-products-gateway.interface.ts`).
Impl today: `AdminProductsLocalGateway` (in-memory + localStorage), injected
**concretely** by `AdminProductsFacade` — no token, so a backend requires
introducing a DI seam first (`BACKEND-AUDIT.md` §14, key finding). DTO:
`AdminProduct` (`src/app/features/admin/products/models/admin-product.model.ts`,
41-field interface incl. `media`, `variants`, `variantAttributes`, `specifications`,
`attributes`, `translations`, `seo`, `badges`, flags, `reviews`, `questions`).
| # | Method | Path (PROPOSED) | Gateway method | Success |
|---|---|---|---|---|
| AP1 | GET | `{base}/backoffice/products?search&categoryId&visibility&stock&includeArchived&sort&page&pageSize` | `loadProducts(filters)` | `200 AdminProductsListResult` |
| AP2 | GET | `{base}/backoffice/products/{id}` | `loadProduct(id)` | `200 AdminProduct` / `404` |
| AP3 | GET | `{base}/backoffice/products/categories` | `loadCategories()` | `200 AdminProductCategoryOption[]` (`{id,title}`) |
| AP4 | POST | `{base}/backoffice/products` | `createProduct(product)` | `201 AdminProduct` |
| AP5 | PUT | `{base}/backoffice/products/{id}` | `updateProduct(product)` | `200 AdminProduct` |
| AP6 | DELETE | `{base}/backoffice/products/{id}` | `deleteProduct(id)` | `204` |
| AP7 | POST | `{base}/backoffice/products/{id}/duplicate` | `duplicateProduct(id)` | `201 AdminProduct` / `404` |
| AP8 | POST | `{base}/backoffice/products/{id}/archive` | `archiveProduct(id)` | `204` |
| AP9 | POST | `{base}/backoffice/products/{id}/restore` | `restoreProduct(id)` | `200 AdminProduct` / `404` |
- **Headers/JWT:** admin auth (§3.0). Today `AdminWebSessionID`; target Bearer JWT.
- **Permission (Proposed):** read (AP1-AP3) → `backoffice.read`; writes
(AP4-AP9) → `backoffice.write`. Finer than that = Requires backend decision.
- **AP1 query params** (from `AdminProductListFilters`): `search` (string),
`categoryId` (string|null), `visibility``all|visible|hidden`, `stock`
`all|in_stock|low_stock|out_of_stock`, `includeArchived` (bool), `sort`
`title|price|priority|stock|updated`, `page` (number), `pageSize` (number).
This is the full pagination + sort + filter + search surface — no other
sort keys exist. Response `AdminProductsListResult { items, total, page, pageSize }`.
- **AP4/AP5 request body:** an `AdminProduct`. By the category precedent
(`AdminCategoriesApiGateway.createCategory` strips `id, itemsCount, deletedAt,
createdAt, updatedAt`), the create body should **omit server-owned fields**
(`id`, `createdAt`, `updatedAt`; also `reviews`/`questions` which are derived).
Update sends the full object with `id` in the path. **Requires backend
decision:** exact server-owned field list for products (the frontend local
gateway does not enforce one).
- **Validation:** none client-side (no `Validators`). Model non-nullable fields
(`name`, `slug`, `sku`, `categoryId`, `price`, `currency`, `quantity`) are the
only implicit "required" signal. Uniqueness of `slug`/`sku`, price ≥ 0, etc.
are all **Requires backend decision** (no `isSlugTaken` equivalent exists for
products, unlike categories). Recommended `422 VALIDATION_FAILED` with
`details[].field` for `sku`/`slug`/`price`.
- **Error responses:** `404` (AP2/AP5-AP9 unknown id), `409 CONFLICT`
(duplicate slug/sku on AP4/AP5 — Requires backend decision, no proactive
pre-check exists), `422` (field validation), plus the common set.
- **Example AP4 request (trimmed):**
```json
{ "name": "Phone X", "slug": "phone-x", "sku": "PHN-X-001", "barcode": "", "brand": "Acme", "categoryId": "3", "visible": true, "archived": false, "priority": 0, "price": 62560, "discount": 0, "currency": "RUB", "quantity": 100, "stockStatus": "in_stock", "media": { "images": [], "gallery": [], "videos": [] }, "specifications": [], "attributes": [], "variantAttributes": [], "variants": [], "relatedProductIds": [], "translations": {}, "seo": { "metaTitle": "", "metaDescription": "", "keywords": "" }, "badges": [] }
```
- **Example AP1 response:**
```json
{ "items": [ { "id": "p_1024", "name": "Phone X", "slug": "phone-x", "sku": "PHN-X-001", "categoryId": "3", "price": 62560, "currency": "RUB", "quantity": 100, "stockStatus": "in_stock", "visible": true, "archived": false } ], "total": 213, "page": 1, "pageSize": 20 }
```
---
### 3.2 Categories
Three surfaces: storefront read (LIVE, legacy + clean stack), and admin CRUD
(the **only** admin domain with a real HTTP impl).
#### 3.2.a Storefront category read — LIVE
| # | Method | Path (LIVE literal) | Backed by | Success |
|---|---|---|---|---|
| C1 | GET | `{base}/category` | `ApiService.getCategories()` (legacy) and `ApiCategoryRepository` (clean stack, `CATEGORY_REPOSITORY`, retry x2) | `200` array of category wire objects |
- Two parallel stacks read the same endpoint (`BACKEND-AUDIT.md` §8). Legacy
wire shape: `Category`/`Subcategory` (`src/app/models/category.model.ts`) via
`normalizeCategory()`. Clean wire DTO: `CategoryDto`/`CategoryNameDto`
(`src/app/core/categories/dto/category.dto.ts`) via `CategoryMapper`. Both
tolerate `names[]` (multi-lang, incl. the `valuue` typo variant), nested
`subcategories[]`, `img``icon`, string↔numeric ids.
- **Headers/JWT:** marketplace headers, no JWT, public.
- **Response `CategoryDto` fields:** `categoryID?`, `parentID?`, `name?`,
`icon?`, `img?`, `priority?`, `visible?`, `categoriesCount?`, `itemCount?`,
`names?: [{ language, value|valuue }]`, `subcategories?: CategoryDto[]`.
- **Error responses:** `500/503` → falls through to empty categories (clean
stack retries twice first). No 404 (collection endpoint).
- **Example response:**
```json
[ { "categoryID": 3, "parentID": 0, "name": "Phones", "icon": "https://…/phones.svg", "priority": 10, "visible": true, "itemCount": 213, "names": [ { "language": "RU", "value": "Телефоны" }, { "language": "EN", "value": "Phones" } ], "subcategories": [] } ]
```
- **Note (`BACKEND-AUDIT.md` §8):** two different `Category` TS types exist —
flag for backend/naming reconciliation.
#### 3.2.b Admin categories CRUD — LIVE (real HTTP gateway exists)
Contract: `AdminCategoriesGateway`. **Real impl:** `AdminCategoriesApiGateway`
(literal paths below — the canonical precedent for all other admin domains).
Mock impl: `AdminCategoriesLocalGateway`. Bound via `ADMIN_CATEGORIES_GATEWAY`
token (MOCK-SWAPPABLE — this domain *can* be rebound to the real gateway today).
DTO: `AdminCategory` (`src/app/features/admin/categories/models/admin-category.model.ts`).
| # | Method | Path (LIVE literal) | Gateway method | Success |
|---|---|---|---|---|
| AC1 | GET | `{base}/backoffice/categories?search&visibility&includeDeleted` | `loadCategories(filters)` | `200 AdminCategory[]` |
| AC2 | GET | `{base}/backoffice/categories/{id}` | `loadCategory(id)` | `200 AdminCategory` / `null` on error |
| AC3 | POST | `{base}/backoffice/categories` | `createCategory(cat)` | `201 AdminCategory` |
| AC4 | PUT | `{base}/backoffice/categories/{id}` | `updateCategory(cat)` | `200 AdminCategory` |
| AC5 | DELETE | `{base}/backoffice/categories/{id}` | `deleteCategory(id)` | `204` (**soft** delete) |
| AC6 | POST | `{base}/backoffice/categories/{id}/restore` | `restoreCategory(id)` | `200 AdminCategory` / `null` |
| AC7 | GET | `{base}/backoffice/categories/slug-taken?slug&excludingId` | `isSlugTaken(slug,excludingId)` | `200 { taken: boolean }` |
- **Headers/JWT:** admin auth (§3.0).
- **Permission (Proposed):** AC1/AC2/AC7 → `backoffice.read`; AC3-AC6 →
`backoffice.write`.
- **AC1 query params** (`AdminCategoryListFilters`): `search` (string),
`visibility``all|visible|hidden`, `includeDeleted` (bool sent as string).
**No pagination** — returns a plain array. No sort param.
- **AC3 request body:** `AdminCategory` **minus** `id, itemsCount, deletedAt,
createdAt, updatedAt` (server-owned — stripped by the real gateway, line 38).
AC4 sends the full object, `id` in path.
- **AC7:** proactive slug-uniqueness pre-check the frontend calls *before*
submit (`excludingId` omitted on create, set to the editing id on update). This
is the only proactive conflict check in the whole admin surface. On network
docs: finalize BACKEND.md as a self-contained implementation prompt Goal: this document alone, pasted into a fresh Claude session, should be enough for a backend dev to implement against without getting stuck or inventing conventions the frontend doesn't actually need. Fixed a real bug: ~65 cross-references throughout the document pointed to docs/AUTHENTICATION.md, docs/ERROR_CONTRACT.md, docs/MAINTENANCE_MODE.md - three sibling docs that were deleted and fully merged into this document's own §4/§6/§10 during the earlier doc-consolidation pass, but the in-text references were never updated. A fresh agent following those links would hit dead ends repeatedly. Bulk-replaced with in- document section references; hand-fixed ~4 sentences that framed §6/ §10 as "sibling task, in progress" (stale - both are complete, this document's §6/§10 already are the settled contract, nothing to wait on). Added "Recommended default" to every item across the document's 5 consolidated "Requires backend decision" registers (§2.12 framework, §3.21 CRUD cross-cutting - 18 items, §4 §12 auth open items - 9 items, §6 error-model summary - 9 items, §10 maintenance-mode list - 5 items). Each default is derived from what the frontend already implies or standard REST/security convention - no invented APIs or business rules. ~6 items are explicitly flagged as real business/security decisions instead (order state-machine rules, Ed25519 cutover strategy, refresh-token reuse-detection posture) since those carry consequences no amount of frontend-code-reading can resolve. Added a "How to use this document" preamble up front: work in §9's dependency order not document order, apply recommended defaults and keep moving, only stop for the explicitly-flagged business/security items, don't invent beyond what's written or directly implied. Frontend side: confirmed nothing else is left. docs/TODO.md already has zero blockers; docs/KNOWN-ISSUES.md's one open item (unreachable Ed25519 error-code UI) is correctly left open and documented rather than faked closed - fixing it needs a real backend emitting real distinguishable error codes, which doesn't exist yet and can't be fabricated without inventing an API contract. No frontend code touched. No architecture changed. No APIs invented.
2026-07-26 16:10:33 +04:00
error it returns `false` (best-effort, TOCTOU-prone — `§6` §409).
- **Validation:** slug uniqueness via AC7; everything else none client-side.
`title`, `slug` are non-nullable in the model. Recommended `409 CONFLICT` on
docs: finalize BACKEND.md as a self-contained implementation prompt Goal: this document alone, pasted into a fresh Claude session, should be enough for a backend dev to implement against without getting stuck or inventing conventions the frontend doesn't actually need. Fixed a real bug: ~65 cross-references throughout the document pointed to docs/AUTHENTICATION.md, docs/ERROR_CONTRACT.md, docs/MAINTENANCE_MODE.md - three sibling docs that were deleted and fully merged into this document's own §4/§6/§10 during the earlier doc-consolidation pass, but the in-text references were never updated. A fresh agent following those links would hit dead ends repeatedly. Bulk-replaced with in- document section references; hand-fixed ~4 sentences that framed §6/ §10 as "sibling task, in progress" (stale - both are complete, this document's §6/§10 already are the settled contract, nothing to wait on). Added "Recommended default" to every item across the document's 5 consolidated "Requires backend decision" registers (§2.12 framework, §3.21 CRUD cross-cutting - 18 items, §4 §12 auth open items - 9 items, §6 error-model summary - 9 items, §10 maintenance-mode list - 5 items). Each default is derived from what the frontend already implies or standard REST/security convention - no invented APIs or business rules. ~6 items are explicitly flagged as real business/security decisions instead (order state-machine rules, Ed25519 cutover strategy, refresh-token reuse-detection posture) since those carry consequences no amount of frontend-code-reading can resolve. Added a "How to use this document" preamble up front: work in §9's dependency order not document order, apply recommended defaults and keep moving, only stop for the explicitly-flagged business/security items, don't invent beyond what's written or directly implied. Frontend side: confirmed nothing else is left. docs/TODO.md already has zero blockers; docs/KNOWN-ISSUES.md's one open item (unreachable Ed25519 error-code UI) is correctly left open and documented rather than faked closed - fixing it needs a real backend emitting real distinguishable error codes, which doesn't exist yet and can't be fabricated without inventing an API contract. No frontend code touched. No architecture changed. No APIs invented.
2026-07-26 16:10:33 +04:00
AC3/AC4 slug collision as a race backstop (`§6` §409), and
`422` for field errors.
- **Error responses:** AC2/AC6 swallow errors → `null` (so a `404` there renders
as "not found"/empty, not an error screen). AC5 hard `404` if unknown. `409`
slug conflict. Common set otherwise.
- **Example AC3 request:**
```json
{ "parentId": null, "title": "Phones", "slug": "phones", "description": "", "icon": "", "imageUrl": "", "imageAlt": "", "order": 0, "visible": true, "status": "published", "translations": {}, "seo": { "metaTitle": "", "metaDescription": "", "keywords": "" }, "attributes": [] }
```
- **Example AC7 response:** `{ "taken": true }`
- **Note:** categories have **no hard delete** — only soft delete + restore
(gateway doc comment). `deletedAt` timestamps the soft delete.
---
### 3.3 Orders
Two surfaces: **checkout order creation** (LIVE, storefront) and **admin order
management** (MOCK-ONLY, no seam). These are separate models — the storefront
`CreateOrderRequest`/`CreateOrderResponse` (inline in `api.service.ts`) is not the
admin `AdminOrder`.
#### 3.3.a Cart → payment → order creation — LIVE
Cart contents are **LOCAL-ONLY** (`CartService`, localStorage key
`marketplace_cart` + Telegram CloudStorage — no cart backend). Checkout produces
these live calls (`BACKEND-AUDIT.md` §10). Payment DTOs are inline in
`src/app/services/api.service.ts`.
| # | Method | Path | Base | Request → Response |
|---|---|---|---|---|
| O1 | POST | `/cart` | marketplace | `CartPaymentRequest``QrCreateResponse` (creates the QR/card payment) |
| O2 | POST | `/qr` | qrApiUrl (headers `authorization-key`, `userid-value`) | `QrCreateRequest``QrCreateResponse` |
| O3 | GET | `/qr/dynamic/{partnerId}/{qrId}` | qrApiUrl | → `QrDynamicStatusResponse` (poll) |
| O4 | GET | `/card/{partnerId}/{orderId}` | qrApiUrl | → `QrDynamicStatusResponse` (poll) |
| O5 | POST | `/orders` | marketplace | `CreateOrderRequest``CreateOrderResponse` (records paid cart; **fire-and-forget** after payment) |
| O6 | POST | `/purchase-email` | marketplace | email/phone + items → `{ message }` (receipt) |
| O7 | POST | `/websession/{sessionId}` | marketplace | item array → any (server-side cart mirror) |
- `partnerId` const = `web-97ec-9c57-4dde-9037-3a68f7f83750`.
- **Headers/JWT:** marketplace headers (O1/O5/O6/O7); O2-O4 use the payment API
with `authorization-key`/`userid-value`. No JWT; customer session via
`WebSessionID`.
- **`CartPaymentRequest` (O1):** `{ amount, currency:'RUB', siteuserID,
siteorderID, redirectUrl, telegramUsername, paymentMethod:'qr'|'card',
qrDescription?, customerID?, items:[{ itemID, price, name, quantity?,
delivery? }] }`.
- **`CreateOrderRequest` (O5):** `{ items:[{ productId, name, quantity, price }],
customer:{ name, email, phone }, payment?:{ method, currency }, shipping?:{
address, method, trackingNumber } }`.
- **`CreateOrderResponse` (O5):** `{ id, orderNumber, status, total, currency }`.
- **`QrCreateResponse`** is a very tolerant union (`api.service.ts` 21-41) —
resolvers pick the first present of `qrId/qrID/nspkID/…`. A backend can emit
any of the documented aliases.
- **Error responses:** O5 is fire-and-forget — a failure must **never** block the
payment-confirmed flow (source comment, line 635). `401` on a customer-facing
docs: finalize BACKEND.md as a self-contained implementation prompt Goal: this document alone, pasted into a fresh Claude session, should be enough for a backend dev to implement against without getting stuck or inventing conventions the frontend doesn't actually need. Fixed a real bug: ~65 cross-references throughout the document pointed to docs/AUTHENTICATION.md, docs/ERROR_CONTRACT.md, docs/MAINTENANCE_MODE.md - three sibling docs that were deleted and fully merged into this document's own §4/§6/§10 during the earlier doc-consolidation pass, but the in-text references were never updated. A fresh agent following those links would hit dead ends repeatedly. Bulk-replaced with in- document section references; hand-fixed ~4 sentences that framed §6/ §10 as "sibling task, in progress" (stale - both are complete, this document's §6/§10 already are the settled contract, nothing to wait on). Added "Recommended default" to every item across the document's 5 consolidated "Requires backend decision" registers (§2.12 framework, §3.21 CRUD cross-cutting - 18 items, §4 §12 auth open items - 9 items, §6 error-model summary - 9 items, §10 maintenance-mode list - 5 items). Each default is derived from what the frontend already implies or standard REST/security convention - no invented APIs or business rules. ~6 items are explicitly flagged as real business/security decisions instead (order state-machine rules, Ed25519 cutover strategy, refresh-token reuse-detection posture) since those carry consequences no amount of frontend-code-reading can resolve. Added a "How to use this document" preamble up front: work in §9's dependency order not document order, apply recommended defaults and keep moving, only stop for the explicitly-flagged business/security items, don't invent beyond what's written or directly implied. Frontend side: confirmed nothing else is left. docs/TODO.md already has zero blockers; docs/KNOWN-ISSUES.md's one open item (unreachable Ed25519 error-code UI) is correctly left open and documented rather than faked closed - fixing it needs a real backend emitting real distinguishable error codes, which doesn't exist yet and can't be fabricated without inventing an API contract. No frontend code touched. No architecture changed. No APIs invented.
2026-07-26 16:10:33 +04:00
`/cart`/`/orders` has **no unified re-auth UX** today (`§6` §401,
Requires backend decision). Payment status polling handles its own timeouts.
- **Validation:** none client-side. Amounts/quantities ≥ 0, valid email/phone =
Requires backend decision.
- **Example O5 request:**
```json
{ "items": [ { "productId": "1024", "name": "Phone X", "quantity": 1, "price": 62560 } ], "customer": { "name": "Ivan", "email": "ivan@example.com", "phone": "+79990000000" }, "payment": { "method": "qr", "currency": "RUB" } }
```
- **Example O5 response:** `{ "id": "ord_88", "orderNumber": "R-000088", "status": "pending", "total": 62560, "currency": "RUB" }`
#### 3.3.b Admin orders management — PROPOSED (MOCK-ONLY, no seam)
Contract: `AdminOrdersGateway`. Impl: `AdminOrdersLocalGateway` (injected
concretely by `AdminOrdersFacade`, and reused by `AdminCustomersFacade` and
`AdminAnalyticsFacade`). DTO: `AdminOrder`
(`src/app/features/admin/orders/models/admin-order.model.ts`).
| # | Method | Path (PROPOSED) | Gateway method | Success |
|---|---|---|---|---|
| AO1 | GET | `{base}/backoffice/orders?search&status&page&pageSize` | `loadOrders(filters)` | `200 AdminOrdersListResult` |
| AO2 | GET | `{base}/backoffice/orders/{id}` | `loadOrder(id)` | `200 AdminOrder` / `404` |
| AO3 | PATCH | `{base}/backoffice/orders/{id}/status` | `updateStatus(id,status)` | `200 AdminOrder` |
| AO4 | POST | `{base}/backoffice/orders/{id}/refund` | `requestRefund(id)` | `200 AdminOrder` |
| AO5 | POST | `{base}/backoffice/orders/{id}/notes` | `addNote(id,note,internal)` | `200 AdminOrder` |
| AO6 | POST | `{base}/backoffice/orders/{id}/archive` | `archiveOrder(id)` | `200 AdminOrder` |
| AO7 | POST | `{base}/backoffice/orders/{id}/restore` | `restoreOrder(id)` | `200 AdminOrder` |
| AO8 | DELETE | `{base}/backoffice/orders/{id}` | `deleteOrder(id)` | `204` |
- **Headers/JWT:** admin auth (§3.0).
- **Permission (Proposed):** AO1/AO2 → `backoffice.read`; AO3-AO8 →
`backoffice.write`.
- **AO1 query params** (`AdminOrderListFilters`): `search`, `status`
`all|pending|processing|shipped|delivered|cancelled|refunded`, `page`,
`pageSize`. Response `AdminOrdersListResult { items, total, page, pageSize }`.
No sort param.
- **AO3 body:** `{ status: AdminOrderStatus }` where status ∈ the 6-value union
above. **State-transition rules** (which transitions are legal, e.g.
`delivered → pending`?) are **Requires backend decision** — the frontend
enforces none. `payment.status` (`unpaid|paid|refund_requested|refunded`) is a
separate axis; AO4 (`requestRefund`) is what moves it to `refund_requested`.
- **AO5 body:** `{ note: string, internal: boolean }``internal:true` writes
`internalNotes`, else `notes` (both plain strings on `AdminOrder`). Appends a
timeline entry.
- **`AdminOrder` timeline:** `timeline: [{ status, timestamp, eventKey:
'created'|'statusChanged'|'refundRequested' }]` — server should append on each
mutation.
- **Validation:** none client-side. Legal status transitions, refund
eligibility = Requires backend decision.
- **Error responses:** `404` (unknown id), `409` (illegal transition — proposed),
common set.
- **Example AO3 request:** `PATCH …/orders/ord_88/status` body `{ "status": "shipped" }`
- **Example AO2 response (trimmed):**
```json
{ "id": "ord_88", "orderNumber": "R-000088", "status": "shipped", "customer": { "name": "Ivan", "email": "ivan@example.com", "phone": "+79990000000" }, "payment": { "method": "qr", "status": "paid", "amount": 62560, "currency": "RUB" }, "shipping": { "address": "…", "method": "courier", "trackingNumber": "TRK1" }, "items": [ { "productId": "1024", "name": "Phone X", "quantity": 1, "price": 62560 } ], "total": 62560, "currency": "RUB", "notes": "", "internalNotes": "", "timeline": [ { "status": "pending", "timestamp": "2026-07-20T10:00:00Z", "eventKey": "created" } ], "archived": false, "createdAt": "2026-07-20T10:00:00Z", "updatedAt": "2026-07-21T09:00:00Z" }
```
---
### 3.4 Customers — PROPOSED (derived, no gateway of its own)
Per `BACKEND-AUDIT.md` §14 and the model comment
(`src/app/features/admin/customers/models/admin-customer.model.ts`): a customer is
**not a stored entity** — `AdminCustomersFacade` derives customers by grouping
`AdminOrder` records by `customer.email` (it injects `AdminOrdersLocalGateway`).
Every `AdminCustomer` field is a real aggregate over that customer's orders, never
fabricated.
**Frontend implication:** there is **no customer CRUD in the frontend at all**
no create/update/delete, only a derived list/detail. A backend can satisfy this
either by (a) exposing a real customers endpoint returning the aggregate shape, or
(b) letting the frontend keep deriving from orders. This is **Requires backend
decision** (whether customers become a first-class backend resource).
| # | Method | Path (PROPOSED) | Purpose | Success |
|---|---|---|---|---|
| CU1 | GET | `{base}/backoffice/customers` | List (derived aggregate) | `200 AdminCustomer[]` |
| CU2 | GET | `{base}/backoffice/customers/{email}` | Detail (aggregate + orders) | `200 AdminCustomer` / `404` |
- **Key = `email`** (customers are keyed by email, not an id).
- **`AdminCustomer`:** `{ email, name, phone, orderCount, totalSpent, currency,
firstOrderAt, lastOrderAt, addresses[], orders: AdminOrder[] }`.
- **Headers/JWT:** admin auth (§3.0). Permission (Proposed): `backoffice.read`.
- **Pagination/sort/filter:** none in the model (plain array). Derivation is
entirely client-side today.
- **Validation:** n/a (read-only, derived).
- **Error responses:** common read set; `404` if a real customers endpoint is
built and the email is unknown.
- **Example CU1 response (trimmed):**
```json
[ { "email": "ivan@example.com", "name": "Ivan", "phone": "+79990000000", "orderCount": 3, "totalSpent": 187680, "currency": "RUB", "firstOrderAt": "2026-01-10T…", "lastOrderAt": "2026-07-20T…", "addresses": ["…"], "orders": [] } ]
```
---
### 3.5 Users — PROPOSED (MOCK-ONLY, no seam)
Contract: `AdminUsersGateway`
(`src/app/features/admin/users/services/admin-users-gateway.interface.ts`). Impl:
`AdminUsersLocalGateway` (injected concretely by `AdminUsersFacade`). DTOs in
`admin-user.model.ts`: `AdminUser`, `AdminRole`, `AdminInvitation`, `AdminSession`,
`AdminUserAuditEntry`.
| # | Method | Path (PROPOSED) | Gateway method | Success |
|---|---|---|---|---|
| U1 | GET | `{base}/backoffice/users` | `loadUsers()` | `200 AdminUser[]` |
| U2 | GET | `{base}/backoffice/roles` | `loadRoles()` | `200 AdminRole[]` |
| U3 | GET | `{base}/backoffice/invitations` | `loadInvitations()` | `200 AdminInvitation[]` |
| U4 | GET | `{base}/backoffice/users/{userId}/sessions` | `loadSessions(userId)` | `200 AdminSession[]` |
| U5 | GET | `{base}/backoffice/users/{userId}/audit` | `loadAudit(userId)` | `200 AdminUserAuditEntry[]` |
| U6 | PATCH | `{base}/backoffice/users/{userId}/role` | `setUserRole(userId,roleId)` | `200 AdminUser` |
| U7 | PATCH | `{base}/backoffice/users/{userId}/status` | `setUserStatus(userId,status)` | `200 AdminUser` |
| U8 | POST | `{base}/backoffice/invitations` | `inviteUser(email,roleId,scope)` | `201 AdminInvitation` |
| U9 | DELETE | `{base}/backoffice/invitations/{id}` | `revokeInvitation(id)` | `204` |
| U10 | DELETE | `{base}/backoffice/sessions/{sessionId}` | `revokeSession(sessionId)` | `204` |
- **Headers/JWT:** admin auth (§3.0).
- **Permission (Proposed):** all user/role/invitation/session management →
`users.manage` (the one permission specifically about this domain — see
docs: finalize BACKEND.md as a self-contained implementation prompt Goal: this document alone, pasted into a fresh Claude session, should be enough for a backend dev to implement against without getting stuck or inventing conventions the frontend doesn't actually need. Fixed a real bug: ~65 cross-references throughout the document pointed to docs/AUTHENTICATION.md, docs/ERROR_CONTRACT.md, docs/MAINTENANCE_MODE.md - three sibling docs that were deleted and fully merged into this document's own §4/§6/§10 during the earlier doc-consolidation pass, but the in-text references were never updated. A fresh agent following those links would hit dead ends repeatedly. Bulk-replaced with in- document section references; hand-fixed ~4 sentences that framed §6/ §10 as "sibling task, in progress" (stale - both are complete, this document's §6/§10 already are the settled contract, nothing to wait on). Added "Recommended default" to every item across the document's 5 consolidated "Requires backend decision" registers (§2.12 framework, §3.21 CRUD cross-cutting - 18 items, §4 §12 auth open items - 9 items, §6 error-model summary - 9 items, §10 maintenance-mode list - 5 items). Each default is derived from what the frontend already implies or standard REST/security convention - no invented APIs or business rules. ~6 items are explicitly flagged as real business/security decisions instead (order state-machine rules, Ed25519 cutover strategy, refresh-token reuse-detection posture) since those carry consequences no amount of frontend-code-reading can resolve. Added a "How to use this document" preamble up front: work in §9's dependency order not document order, apply recommended defaults and keep moving, only stop for the explicitly-flagged business/security items, don't invent beyond what's written or directly implied. Frontend side: confirmed nothing else is left. docs/TODO.md already has zero blockers; docs/KNOWN-ISSUES.md's one open item (unreachable Ed25519 error-code UI) is correctly left open and documented rather than faked closed - fixing it needs a real backend emitting real distinguishable error codes, which doesn't exist yet and can't be fabricated without inventing an API contract. No frontend code touched. No architecture changed. No APIs invented.
2026-07-26 16:10:33 +04:00
`§4` §9.1). Read-only U1-U5 could be `backoffice.read` — Requires
backend decision.
- **U6 body:** `{ roleId: string }`. **U7 body:** `{ status: AdminUserStatus }`
where status ∈ `active|invited|suspended`. **U8 body:** `{ email, roleId,
scope }` where scope ∈ `marketplace|office`.
- **`AdminRole`** here is `{ id, name, permissions: string[], builtIn: boolean }`
**a different type from the auth `AdminRole` string-union** used by the JWT
`role` claim (`Owner|Administrator|Editor|Support|ReadOnly`,
`src/app/core/auth/models/permission.model.ts`). **This is the flagged
docs: finalize BACKEND.md as a self-contained implementation prompt Goal: this document alone, pasted into a fresh Claude session, should be enough for a backend dev to implement against without getting stuck or inventing conventions the frontend doesn't actually need. Fixed a real bug: ~65 cross-references throughout the document pointed to docs/AUTHENTICATION.md, docs/ERROR_CONTRACT.md, docs/MAINTENANCE_MODE.md - three sibling docs that were deleted and fully merged into this document's own §4/§6/§10 during the earlier doc-consolidation pass, but the in-text references were never updated. A fresh agent following those links would hit dead ends repeatedly. Bulk-replaced with in- document section references; hand-fixed ~4 sentences that framed §6/ §10 as "sibling task, in progress" (stale - both are complete, this document's §6/§10 already are the settled contract, nothing to wait on). Added "Recommended default" to every item across the document's 5 consolidated "Requires backend decision" registers (§2.12 framework, §3.21 CRUD cross-cutting - 18 items, §4 §12 auth open items - 9 items, §6 error-model summary - 9 items, §10 maintenance-mode list - 5 items). Each default is derived from what the frontend already implies or standard REST/security convention - no invented APIs or business rules. ~6 items are explicitly flagged as real business/security decisions instead (order state-machine rules, Ed25519 cutover strategy, refresh-token reuse-detection posture) since those carry consequences no amount of frontend-code-reading can resolve. Added a "How to use this document" preamble up front: work in §9's dependency order not document order, apply recommended defaults and keep moving, only stop for the explicitly-flagged business/security items, don't invent beyond what's written or directly implied. Frontend side: confirmed nothing else is left. docs/TODO.md already has zero blockers; docs/KNOWN-ISSUES.md's one open item (unreachable Ed25519 error-code UI) is correctly left open and documented rather than faked closed - fixing it needs a real backend emitting real distinguishable error codes, which doesn't exist yet and can't be fabricated without inventing an API contract. No frontend code touched. No architecture changed. No APIs invented.
2026-07-26 16:10:33 +04:00
duplicate** (`BACKEND-AUDIT.md` §14, `§4` §9). Reconciliation
needed: treat the auth string-union as the JWT/role-claim contract; treat this
interface as an admin-users-management row. Recommend renaming the latter (e.g.
`AdminUserRoleRecord`). **Requires backend decision / naming reconciliation.**
- **Roles CRUD:** the gateway exposes only `loadRoles()` (read). There is **no
create/update/delete role** endpoint in the frontend — role definitions are
read-only from the frontend's perspective. Whether roles are backend-editable =
Requires backend decision.
- **Sessions/audit:** `revokeSession` (U10) exists as a mock method; whether an
Owner can remotely kill another admin's live session, and how that propagates to
the already-logged-in client, is **Requires backend decision**
docs: finalize BACKEND.md as a self-contained implementation prompt Goal: this document alone, pasted into a fresh Claude session, should be enough for a backend dev to implement against without getting stuck or inventing conventions the frontend doesn't actually need. Fixed a real bug: ~65 cross-references throughout the document pointed to docs/AUTHENTICATION.md, docs/ERROR_CONTRACT.md, docs/MAINTENANCE_MODE.md - three sibling docs that were deleted and fully merged into this document's own §4/§6/§10 during the earlier doc-consolidation pass, but the in-text references were never updated. A fresh agent following those links would hit dead ends repeatedly. Bulk-replaced with in- document section references; hand-fixed ~4 sentences that framed §6/ §10 as "sibling task, in progress" (stale - both are complete, this document's §6/§10 already are the settled contract, nothing to wait on). Added "Recommended default" to every item across the document's 5 consolidated "Requires backend decision" registers (§2.12 framework, §3.21 CRUD cross-cutting - 18 items, §4 §12 auth open items - 9 items, §6 error-model summary - 9 items, §10 maintenance-mode list - 5 items). Each default is derived from what the frontend already implies or standard REST/security convention - no invented APIs or business rules. ~6 items are explicitly flagged as real business/security decisions instead (order state-machine rules, Ed25519 cutover strategy, refresh-token reuse-detection posture) since those carry consequences no amount of frontend-code-reading can resolve. Added a "How to use this document" preamble up front: work in §9's dependency order not document order, apply recommended defaults and keep moving, only stop for the explicitly-flagged business/security items, don't invent beyond what's written or directly implied. Frontend side: confirmed nothing else is left. docs/TODO.md already has zero blockers; docs/KNOWN-ISSUES.md's one open item (unreachable Ed25519 error-code UI) is correctly left open and documented rather than faked closed - fixing it needs a real backend emitting real distinguishable error codes, which doesn't exist yet and can't be fabricated without inventing an API contract. No frontend code touched. No architecture changed. No APIs invented.
2026-07-26 16:10:33 +04:00
(`§4` §8 — no push/poll mechanism exists client-side).
- **Validation:** none client-side. Email format, role existence, self-demotion
guards = Requires backend decision.
- **Error responses:** `404` (unknown user/session/invitation), `409` (e.g.
inviting an already-registered email — proposed), `422` (bad email/role),
common set.
- **Example U8 request:** `{ "email": "new.admin@example.com", "roleId": "role_editor", "scope": "office" }`
- **Example U1 response (trimmed):**
```json
[ { "id": "u_1", "name": "Alice", "telegramUsername": "@alice", "email": "alice@example.com", "scope": "office", "roleId": "role_owner", "status": "active", "lastLoginAt": "2026-07-24T…", "createdAt": "2026-01-01T…" } ]
```
---
### 3.6 Roles
Covered structurally under Users (§3.5, endpoint U2 `loadRoles`). Summary of the
reconciliation the backend must make (this is the audit's flagged item):
- **`AdminRole` #1** — string union `'Owner'|'Administrator'|'Editor'|'Support'
|'ReadOnly'` (`src/app/core/auth/models/permission.model.ts`). Used by the JWT
`role` claim and `PermissionService`. Ordered high→low by convention only. Maps
docs: finalize BACKEND.md as a self-contained implementation prompt Goal: this document alone, pasted into a fresh Claude session, should be enough for a backend dev to implement against without getting stuck or inventing conventions the frontend doesn't actually need. Fixed a real bug: ~65 cross-references throughout the document pointed to docs/AUTHENTICATION.md, docs/ERROR_CONTRACT.md, docs/MAINTENANCE_MODE.md - three sibling docs that were deleted and fully merged into this document's own §4/§6/§10 during the earlier doc-consolidation pass, but the in-text references were never updated. A fresh agent following those links would hit dead ends repeatedly. Bulk-replaced with in- document section references; hand-fixed ~4 sentences that framed §6/ §10 as "sibling task, in progress" (stale - both are complete, this document's §6/§10 already are the settled contract, nothing to wait on). Added "Recommended default" to every item across the document's 5 consolidated "Requires backend decision" registers (§2.12 framework, §3.21 CRUD cross-cutting - 18 items, §4 §12 auth open items - 9 items, §6 error-model summary - 9 items, §10 maintenance-mode list - 5 items). Each default is derived from what the frontend already implies or standard REST/security convention - no invented APIs or business rules. ~6 items are explicitly flagged as real business/security decisions instead (order state-machine rules, Ed25519 cutover strategy, refresh-token reuse-detection posture) since those carry consequences no amount of frontend-code-reading can resolve. Added a "How to use this document" preamble up front: work in §9's dependency order not document order, apply recommended defaults and keep moving, only stop for the explicitly-flagged business/security items, don't invent beyond what's written or directly implied. Frontend side: confirmed nothing else is left. docs/TODO.md already has zero blockers; docs/KNOWN-ISSUES.md's one open item (unreachable Ed25519 error-code UI) is correctly left open and documented rather than faked closed - fixing it needs a real backend emitting real distinguishable error codes, which doesn't exist yet and can't be fabricated without inventing an API contract. No frontend code touched. No architecture changed. No APIs invented.
2026-07-26 16:10:33 +04:00
to permissions via `ROLE_PERMISSIONS` (`§4` §9.1). **This is the
role contract for authorization.**
- **`AdminRole` #2** — interface `{ id, name, permissions: string[], builtIn }`
(`src/app/features/admin/users/models/admin-user.model.ts`). A management row for
the users screen. `AdminUser.roleId` references `AdminRole.id` here (a
string id like `role_owner`), **not** the string-union value.
- **The two do not reference each other.** A backend role table must decide
whether `AdminRole.id` (#2) resolves to one of the five #1 union values (i.e.
`builtIn` roles map onto the permission model) and whether custom
(`builtIn:false`) roles with arbitrary `permissions[]` are allowed. **Requires
backend decision** — the frontend does not resolve this collision.
- **No role CRUD endpoint** exists in the frontend beyond read (`loadRoles`).
Custom-role create/edit/delete = Requires backend decision.
---
### 3.7 Media — PROPOSED (MOCK-SWAPPABLE via abstract-class token)
Contract: abstract class `MediaRepository` (`src/app/core/media/media-repository.ts`).
Bound in `app.config.ts``MockMediaRepository` (`useClass`). MOCK-SWAPPABLE (a
real impl drops in via the same class token — no new seam needed). DTO:
`MediaAsset` (`src/app/core/media/models/media-asset.model.ts`).
**Scope note (per task):** this section covers the **CRUD list/delete/metadata**
endpoints only. Upload *mechanics* (multipart, presigned URLs, chunking) are
deferred to a sibling "Uploads" section of the master doc — here `upload` is
listed for completeness but its transport is out of scope.
| # | Method | Path (PROPOSED) | Repository method | Success |
|---|---|---|---|---|
| M1 | GET | `{base}/media?page&pageSize&search&folder&tag&kind&sort` | `list(params)` | `200 MediaListResult { items, total }` |
| M2 | POST | `{base}/media` (multipart — see Uploads section) | `upload(file,options)` | `201 MediaAsset` |
| M3 | DELETE | `{base}/media/{id}` | `remove(id)` | `204` |
| M4 | PATCH | `{base}/media/{id}` | `update(id,patch)` | `200 MediaAsset` |
| M5 | GET | `{base}/media/folders` | `listFolders()` | `200 string[]` |
- **Headers/JWT:** the `adminAuthHeadersInterceptor` already gates `/media/`
paths (§3.0, anticipatory) — admin auth applies. Permission (Proposed):
`backoffice.read` (M1/M5), `backoffice.write` (M2/M3/M4).
- **M1 query params** (`MediaListParams`): `page?`, `pageSize?`, `search?`,
`folder?`, `tag?`, `kind?``image|svg|pdf|other`, `sort?`
`recent|name|size`. Response `{ items: MediaAsset[], total }` (offset paginated).
- **M4 patch body** — restricted to metadata: `Partial<Pick<MediaAsset, 'altText'
| 'tags' | 'folder' | 'caption' | 'description' | 'decorative'>>`. The
repository signature **forbids** patching `url/filename/mimeType/size/width/
height` — a backend should reject those on PATCH.
- **`MediaAsset`:** `{ id, url, thumbnailUrl?, filename, mimeType, size, width?,
height?, altText?: Record<locale,string>, caption?, description?, decorative?,
tags?, folder?, createdAt }`.
- **Validation:** none client-side. Allowed mime types / max size = Requires
backend decision (also relevant to the Uploads section).
- **Error responses:** `404` (M3/M4 unknown id), `422` (invalid patch field),
common set. `MediaUsageService` exists client-side to warn on in-use assets —
whether delete is blocked when an asset is referenced is Requires backend
decision.
- **Example M4 request:** `PATCH …/media/asset_9` body `{ "altText": { "en": "Product hero", "ru": "…" }, "tags": ["hero"], "decorative": false }`
- **Example M1 response (trimmed):**
```json
{ "items": [ { "id": "asset_9", "url": "https://…/hero.webp", "filename": "hero.webp", "mimeType": "image/webp", "size": 48213, "width": 1200, "height": 630, "folder": "products", "createdAt": "2026-07-01T…" } ], "total": 342 }
```
---
### 3.8 CMS — Static pages — PROPOSED / FUTURE (LOCAL-ONLY today)
**Status: LOCAL-ONLY.** `ContentManagementFacade`
(`src/app/features/content-management/facade/content-management.facade.ts`) →
`ContentPageService` operates entirely on the **already-loaded
`BootstrapConfig.staticPages`** — there are **no dedicated CMS backend calls**
(`BACKEND-AUDIT.md` §16). Editing writes to in-memory bootstrap + localStorage
drafts; "publishing" = writing bootstrap back, for which **no client HTTP call
exists**. So every endpoint below is **PROPOSED / FUTURE**.
Backend-shaped model: `StaticPageConfig` / `StaticPagesConfig` /
`ResolvedStaticPage` (`src/app/shared/models/config/static-page.model.ts`). Editor
view model: `ContentPage` (`src/app/features/content-management/models/content-page.model.ts`);
`ContentPageService` is the adapter (`toBootstrapRecord`/`serializePages`).
| # | Method | Path (PROPOSED / FUTURE) | Purpose | Success |
|---|---|---|---|---|
| CM1 | GET | `{base}/builder/static-pages` | List pages | `200 StaticPagesConfig` |
| CM2 | GET | `{base}/builder/static-pages/{id}` | Single page | `200 StaticPageConfig` / `404` |
| CM3 | POST | `{base}/builder/static-pages` | Create page | `201 StaticPageConfig` |
| CM4 | PUT | `{base}/builder/static-pages/{id}` | Update page | `200 StaticPageConfig` |
| CM5 | DELETE | `{base}/builder/static-pages/{id}` | Delete page | `204` |
- **Reality check:** today the frontend gets these pages **inside `GET /bootstrap`**
and never calls CM1-CM5. If the backend keeps CMS inside bootstrap, these
endpoints may never exist — the alternative is a builder-publish endpoint that
writes the whole bootstrap (see §3.15). **Requires backend decision:**
per-page CMS endpoints vs. whole-bootstrap publish.
- **Headers/JWT:** admin/builder auth (§3.0). Permission (Proposed):
`builder.read` (CM1/CM2), `builder.write` (CM3-CM5).
- **`StaticPageConfig` key fields:** `{ id, slug, title: string|Localized,
showInFooter?, showInHeader?, showInSitemap?, icon?, order?, requiresAuthentication?,
translations?: Record<locale, { title?, html?, seo? }>, html?: string|Localized,
seo?: StaticPageSeoConfig, visible?, enabled?, status?: 'draft'|'published',
heroImage?, gallery?, updatedAt? }`. Two lifecycle switches: `enabled` (master
on/off) and per-page `status` (`draft` never resolves on storefront even if the
bootstrap is published).
- **Validation:** the client-side `ContentPageService.validatePages()` +
`ProjectValidator` convention exists (`ProjectValidationIssue { code, message,
docs: finalize BACKEND.md as a self-contained implementation prompt Goal: this document alone, pasted into a fresh Claude session, should be enough for a backend dev to implement against without getting stuck or inventing conventions the frontend doesn't actually need. Fixed a real bug: ~65 cross-references throughout the document pointed to docs/AUTHENTICATION.md, docs/ERROR_CONTRACT.md, docs/MAINTENANCE_MODE.md - three sibling docs that were deleted and fully merged into this document's own §4/§6/§10 during the earlier doc-consolidation pass, but the in-text references were never updated. A fresh agent following those links would hit dead ends repeatedly. Bulk-replaced with in- document section references; hand-fixed ~4 sentences that framed §6/ §10 as "sibling task, in progress" (stale - both are complete, this document's §6/§10 already are the settled contract, nothing to wait on). Added "Recommended default" to every item across the document's 5 consolidated "Requires backend decision" registers (§2.12 framework, §3.21 CRUD cross-cutting - 18 items, §4 §12 auth open items - 9 items, §6 error-model summary - 9 items, §10 maintenance-mode list - 5 items). Each default is derived from what the frontend already implies or standard REST/security convention - no invented APIs or business rules. ~6 items are explicitly flagged as real business/security decisions instead (order state-machine rules, Ed25519 cutover strategy, refresh-token reuse-detection posture) since those carry consequences no amount of frontend-code-reading can resolve. Added a "How to use this document" preamble up front: work in §9's dependency order not document order, apply recommended defaults and keep moving, only stop for the explicitly-flagged business/security items, don't invent beyond what's written or directly implied. Frontend side: confirmed nothing else is left. docs/TODO.md already has zero blockers; docs/KNOWN-ISSUES.md's one open item (unreachable Ed25519 error-code UI) is correctly left open and documented rather than faked closed - fixing it needs a real backend emitting real distinguishable error codes, which doesn't exist yet and can't be fabricated without inventing an API contract. No frontend code touched. No architecture changed. No APIs invented.
2026-07-26 16:10:33 +04:00
section, fieldKey, severity }`) and is the shape `§6` §422
recommends aligning backend `details[]` to. Actual required fields (slug
uniqueness, non-empty title) = Requires backend decision.
- **Error responses:** `404` (CM2/CM4/CM5), `409` (duplicate slug — proposed),
`422` (validation), common set. None handled specially by the UI today.
- **Example CM4 request (trimmed):**
```json
{ "id": "about", "slug": "about-us", "title": { "en": "About us", "ru": "О нас" }, "showInFooter": true, "status": "published", "enabled": true, "html": { "en": "<h1>About</h1>" }, "seo": { "title": { "en": "About us" }, "robots": "index,follow" } }
```
---
### 3.9 Homepage — PROPOSED / FUTURE (LOCAL-ONLY)
**Status: LOCAL-ONLY** (project-editor, `BACKEND-AUDIT.md` §17). The homepage is a
**composition of `SectionConfig``WidgetConfig`** stored inside
`BootstrapConfig` (`layout`/`pages`). Edited via `ProjectEditorFacade` +
`homepage-section.component.ts` / `homepage-overview.component.ts` against
in-memory bootstrap; persisted only to localStorage drafts. No HTTP save/publish
call exists.
Models: `SectionConfig` (`src/app/shared/models/config/section.model.ts`) —
`{ id, type, order, layout?: { strategy: 'stack'|'grid'|'hero'|'carousel'|'split',
columns?, gap?, align? }, visibility?, widgets: WidgetConfig[], visible? }`.
| # | Method | Path (PROPOSED / FUTURE) | Purpose | Success |
|---|---|---|---|---|
| HP1 | GET | `{base}/builder/homepage` | Load section/widget composition | `200 SectionConfig[]` |
| HP2 | PUT | `{base}/builder/homepage` | Replace composition (reorder, add/remove sections & widgets) | `200 SectionConfig[]` |
- **Why a single PUT, not granular CRUD:** the editor mutates an in-memory tree
and saves it whole (undo/redo over `History<BootstrapConfig>`); there is no
per-section/per-widget network operation in the frontend. A whole-composition
PUT matches how the client actually works. Granular section/widget endpoints
would be new backend design. **Requires backend decision.**
- **Headers/JWT:** builder auth (§3.0). Permission (Proposed): `builder.read`
(HP1), `builder.write` (HP2).
- **Section ordering** is by the numeric `order` field; widgets carry their own
`order` within a section.
- **Validation:** `ProjectValidator` (client-side, localStorage-scoped). Section
`type` allow-list, widget `type` must exist in the widget manifest (§3.10) =
Requires backend decision.
- **Error responses:** `422` (invalid section/widget type or malformed tree),
common set.
- **Example HP2 request (trimmed):**
```json
[ { "id": "hero", "type": "hero", "order": 0, "layout": { "strategy": "hero" }, "visible": true, "widgets": [ { "id": "w1", "type": "hero-banner", "version": "1.0.0", "order": 0, "props": { "title": "Sale" } } ] } ]
```
---
### 3.10 Widgets — PROPOSED / FUTURE (LOCAL-ONLY) + widget manifest (LIVE)
Two things named "widgets": the **manifest** (LIVE) and the **widget instances**
inside sections (LOCAL-ONLY, edited via project-editor `widgets-section.component.ts`).
#### 3.10.a Widget manifest — LIVE
`WidgetManifestService` (`src/app/widgets/registry/widget-manifest.service.ts`):
| # | Method | Path (LIVE) | Success |
|---|---|---|---|
| W1 | GET | `bootstrap.widgetRegistry.manifestUrl` (fallback `/assets/mock/bootstrap/widget-manifest.json`) | `200 WidgetManifestFile` |
- Read-only registry of available widget types + their settings schema
(`WidgetManifestEntry/File`, `WidgetSettingsSchema``src/app/widgets/contracts/widget-manifest.contract.ts`).
- **Headers/JWT:** the manifest URL is bootstrap-driven; public read.
- **Validation/errors:** on failure falls back to the static asset. No mutation.
#### 3.10.b Widget instances CRUD — PROPOSED / FUTURE
Widget instances (`WidgetConfig`, `src/app/shared/models/config/widget.model.ts`)
live inside `SectionConfig.widgets[]` and are created/updated/removed/reordered
purely in-memory via `ProjectEditorFacade` (localStorage drafts). There is **no
per-widget backend call** — they are saved as part of the homepage composition
(§3.9 HP2). So there is **no separate widget CRUD endpoint** unless the backend
chooses granular editing (Requires backend decision). If it does:
| # | Method | Path (PROPOSED / FUTURE) | Purpose |
|---|---|---|---|
| W2 | POST | `{base}/builder/sections/{sectionId}/widgets` | Add widget |
| W3 | PUT | `{base}/builder/sections/{sectionId}/widgets/{widgetId}` | Update widget |
| W4 | DELETE | `{base}/builder/sections/{sectionId}/widgets/{widgetId}` | Remove widget |
- **`WidgetConfig`:** `{ id, type, version, title?, subtitle?, order?, padding?,
visibility?: { desktop?, tablet?, mobile? }, animation?, style?: Record<string,
string>, permissions?: { requireAuthenticated?, roles?, permissions? }, props:
Record<string, unknown>, actions?, featureFlag?, visible? }`.
- **Permission (Proposed):** `builder.write`. **Validation:** widget `type` must
exist in the manifest (W1); `props` shape validated against
`WidgetSettingsSchema`. Both client-side/manifest-driven; backend enforcement =
Requires backend decision.
- **Recommendation:** treat §3.9 HP2 (whole-composition PUT) as canonical and
W2-W4 as optional granular sugar the frontend does not need today.
---
### 3.11 Navigation — PROPOSED / FUTURE (LOCAL-ONLY via builder)
**Status: LOCAL-ONLY.** Header/footer nav is `BootstrapConfig.navigation`
(`src/app/shared/models/config/navigation.model.ts`), edited via
`ProjectEditorFacade` + `navigation-section.component.ts`. Facade methods
(all in-memory + localStorage; `project-editor.facade.ts`): `addNavLink`,
`addStaticPageNavLink`, `removeNavLink`, `updateNavLink`, `updateNavLinkLabel`,
`reorderNavLink` — all take `target: 'header' | 'footer'`. No HTTP call exists.
Model: `NavigationConfig { header: NavigationItemConfig[], footer:
NavigationItemConfig[] | FooterNavigationGroupConfig[], sidebar? }`.
`NavigationItemConfig { id, labelKey?, label?: string|Localized, route?, type?,
key?, icon?, order?, visible?, visibleWhenFlags?, children? }`.
| # | Method | Path (PROPOSED / FUTURE) | Purpose | Success |
|---|---|---|---|---|
| N1 | GET | `{base}/builder/navigation` | Load nav config | `200 NavigationConfig` |
| N2 | PUT | `{base}/builder/navigation` | Replace nav config (add/remove/update/reorder items) | `200 NavigationConfig` |
- **Whole-config PUT** matches the editor (it mutates the nav tree in memory and
saves whole). Granular per-item endpoints = Requires backend decision.
- **Headers/JWT:** builder auth. Permission (Proposed): `builder.read` (N1),
`builder.write` (N2).
- **Ordering:** `reorderNavLink(target, id, direction: -1|1)` moves an item; the
numeric `order` field is authoritative on the wire.
- **Localized labels:** `updateNavLinkLabel(target, id, value, locale?)` writes
into `label` as a `{ [locale]: string }` map.
- **Validation:** none client-side beyond the builder validator. Route validity,
no orphan `children` = Requires backend decision.
- **Errors:** `422` (malformed tree), common set.
- **Example N2 request (trimmed):**
```json
{ "header": [ { "id": "n1", "label": { "en": "Catalog", "ru": "Каталог" }, "route": "/catalog", "order": 0, "visible": true } ], "footer": [] }
```
---
### 3.12 Footer — PROPOSED / FUTURE (LOCAL-ONLY via builder)
**Status: LOCAL-ONLY.** `BootstrapConfig.footer` (`FooterConfig`,
`src/app/shared/models/config/footer-config.model.ts`), edited via
`footer-section.component.ts` + `ProjectEditorFacade`. No HTTP call.
`FooterConfig { logoUrl?, paymentIcons?: [{ src, alt, width?, height? }],
copyrightText?: string|Localized, columns?: [{ id, title, links: [{ id, label,
pageKey?, url? }] }], socialLinks?: [{ id, label, url, icon? }], legalPageKeys?
(deprecated), staticPageKeys? (deprecated) }`.
| # | Method | Path (PROPOSED / FUTURE) | Purpose | Success |
|---|---|---|---|---|
| F1 | GET | `{base}/builder/footer` | Load footer config | `200 FooterConfig` |
| F2 | PUT | `{base}/builder/footer` | Replace footer (payment icons, columns/legal links, copyright, socials) | `200 FooterConfig` |
- **Headers/JWT:** builder auth. Permission (Proposed): `builder.read` (F1),
`builder.write` (F2).
- **Link resolution:** `FooterLinkConfig.pageKey` (preferred, references a CMS
page id — stays correct if the page route changes) vs. `url` (raw external).
`legalPageKeys`/`staticPageKeys` are **deprecated**, superseded by `columns`
a backend should accept them for back-compat but write `columns`.
- **Validation:** none client-side. Payment-icon URL validity, non-empty column
titles = Requires backend decision.
- **Errors:** `422`, common set.
- **Example F2 request (trimmed):**
```json
{ "logoUrl": "https://…/logo.svg", "paymentIcons": [ { "src": "https://…/visa.svg", "alt": "Visa", "width": 40, "height": 24 } ], "copyrightText": { "en": "© 2026 Acme" }, "columns": [ { "id": "legal", "title": "Legal", "links": [ { "id": "l1", "label": "Terms", "pageKey": "terms" } ] } ], "socialLinks": [] }
```
---
### 3.13 Branding / Theme — PROPOSED / FUTURE (LOCAL-ONLY via builder)
**Status: LOCAL-ONLY.** `BootstrapConfig.branding` (`BrandingConfig`,
`src/app/shared/models/config/branding.model.ts`) and `BootstrapConfig.theme`
(`theme.model.ts`), edited via `branding-section.component.ts` /
`theme-section.component.ts` / `brand-overview.component.ts`. No HTTP call.
`BrandingConfig { brandName, legalName, slogan?, logoUrl, logoCompactUrl?,
faviconUrl, appIconUrl?, socialImageUrl?, galleryUrls?, supportEmail?,
supportPhone? }`.
| # | Method | Path (PROPOSED / FUTURE) | Purpose | Success |
|---|---|---|---|---|
| B1 | GET | `{base}/builder/branding` | Load branding + theme | `200 { branding: BrandingConfig, theme: ThemeConfig }` |
| B2 | PUT | `{base}/builder/branding` | Update branding | `200 BrandingConfig` |
| B3 | PUT | `{base}/builder/theme` | Update theme (colors, tokens) | `200 ThemeConfig` |
- **Headers/JWT:** builder auth. Permission (Proposed): `builder.read` (B1),
`builder.write` (B2/B3). Could be `settings.manage` if branding is treated as a
tenant-settings concern — Requires backend decision.
- **Theme model** (`src/app/shared/models/config/theme.model.ts`, not re-listed
here) carries color tokens; the editor computes contrast client-side
(`sections/brand/contrast.util.ts`). Contrast/accessibility is enforced
client-side only.
- **Validation:** none backend-driven. `brandName`, `logoUrl`, `faviconUrl`
non-optional in the model; valid email for `supportEmail` = Requires backend
decision.
- **Errors:** `422`, common set.
- **Example B2 request (trimmed):**
```json
{ "brandName": "Acme", "legalName": "Acme LLC", "logoUrl": "https://…/logo.svg", "faviconUrl": "https://…/favicon.ico", "supportEmail": "help@acme.com" }
```
---
### 3.14 Languages — PROPOSED / FUTURE (LOCAL-ONLY via builder)
**Status: LOCAL-ONLY.** Locale management is `BootstrapConfig.localization` +
`BootstrapConfig.tenant.supportedLocales/defaultLocale`, edited via
`languages-section.component.ts` + `ProjectEditorFacade` methods `addLocale`,
`removeLocale`, `setDefaultLocale` — all mutate in-memory bootstrap only. No HTTP.
`LocalizationConfig { defaultLocale, supportedLocales: string[], currencyByLocale:
Record<locale,currency>, dictionaries: [{ locale, dictionaryUrl, version }] }`.
| # | Method | Path (PROPOSED / FUTURE) | Purpose | Success |
|---|---|---|---|---|
| L1 | GET | `{base}/builder/languages` | Load locale config | `200 LocalizationConfig` |
| L2 | POST | `{base}/builder/languages` | Add a supported locale | `201 LocalizationConfig` |
| L3 | DELETE | `{base}/builder/languages/{code}` | Remove a locale | `200 LocalizationConfig` |
| L4 | PUT | `{base}/builder/languages/default` | Set default locale | `200 LocalizationConfig` |
- **Headers/JWT:** builder auth. Permission (Proposed): `builder.write` for
L2-L4, `builder.read` for L1. Could be `settings.manage` — Requires backend
decision.
- **Client-side rules that a backend should mirror:** locale code is lowercased
and trimmed on add; adding an already-present locale is rejected client-side
with `builder.languageAlreadyAdded` (so **`409 CONFLICT`** on L2 duplicate is
the right backstop). L2 body: `{ code: string }` (a locale like `en`, `ru`,
`hy`). L4 body: `{ code: string }` — must be an already-supported locale.
- **Removing a locale** shows a confirm dialog (destructive: strips that locale's
content). Whether removing the *default* locale is allowed = Requires backend
decision (the UI lets you set a new default first).
- **Validation:** non-empty lowercased code; membership check for default. Valid
BCP-47 code = Requires backend decision.
- **Errors:** `409` (duplicate add), `422` (invalid code / removing last locale),
common set.
- **Example L2 request:** `{ "code": "hy" }`
---
### 3.15 Marketplace Settings — PROPOSED / FUTURE
There is **no dedicated "settings" admin gateway or page** beyond the builder
sections already covered (§3.8-3.14) and `BootstrapConfig`'s remaining sub-configs
(`featureFlags`, `features`, `seo`, `catalog`, `header`, `layout`,
`productPage`, `userExperience`, `company`, `apiEndpoints`, `permissions`). All of
these are **served inside `GET /bootstrap`** and edited (where editable) via the
project-editor in-memory (LOCAL-ONLY). The only permission that names settings is
docs: finalize BACKEND.md as a self-contained implementation prompt Goal: this document alone, pasted into a fresh Claude session, should be enough for a backend dev to implement against without getting stuck or inventing conventions the frontend doesn't actually need. Fixed a real bug: ~65 cross-references throughout the document pointed to docs/AUTHENTICATION.md, docs/ERROR_CONTRACT.md, docs/MAINTENANCE_MODE.md - three sibling docs that were deleted and fully merged into this document's own §4/§6/§10 during the earlier doc-consolidation pass, but the in-text references were never updated. A fresh agent following those links would hit dead ends repeatedly. Bulk-replaced with in- document section references; hand-fixed ~4 sentences that framed §6/ §10 as "sibling task, in progress" (stale - both are complete, this document's §6/§10 already are the settled contract, nothing to wait on). Added "Recommended default" to every item across the document's 5 consolidated "Requires backend decision" registers (§2.12 framework, §3.21 CRUD cross-cutting - 18 items, §4 §12 auth open items - 9 items, §6 error-model summary - 9 items, §10 maintenance-mode list - 5 items). Each default is derived from what the frontend already implies or standard REST/security convention - no invented APIs or business rules. ~6 items are explicitly flagged as real business/security decisions instead (order state-machine rules, Ed25519 cutover strategy, refresh-token reuse-detection posture) since those carry consequences no amount of frontend-code-reading can resolve. Added a "How to use this document" preamble up front: work in §9's dependency order not document order, apply recommended defaults and keep moving, only stop for the explicitly-flagged business/security items, don't invent beyond what's written or directly implied. Frontend side: confirmed nothing else is left. docs/TODO.md already has zero blockers; docs/KNOWN-ISSUES.md's one open item (unreachable Ed25519 error-code UI) is correctly left open and documented rather than faked closed - fixing it needs a real backend emitting real distinguishable error codes, which doesn't exist yet and can't be fabricated without inventing an API contract. No frontend code touched. No architecture changed. No APIs invented.
2026-07-26 16:10:33 +04:00
`settings.manage` (Owner-only, `§4` §9.1).
The unifying gap: **there is no client-side "publish/save bootstrap" HTTP call
anywhere** (`BACKEND-AUDIT.md` §17, §25.5). Everything in §3.8-3.15 is persisted
only to localStorage drafts + in-memory bootstrap. A real backend needs **one of**:
| # | Method | Path (PROPOSED / FUTURE) | Purpose | Success |
|---|---|---|---|---|
| S1 | GET | `{base}/bootstrap` | Load full config (**LIVE** — this one exists) | `200 BootstrapConfig` |
| S2 | PUT | `{base}/builder/bootstrap` (or `/builder/publish`) | Persist the edited bootstrap (publish) | `200 BootstrapConfig` |
| S3 | GET | `{base}/builder/settings` | General tenant settings (feature flags, catalog, SEO) | `200` (subset of BootstrapConfig) |
| S4 | PUT | `{base}/builder/settings` | Update general settings | `200` |
- **S1 is the only LIVE endpoint here** (`ApiBootstrapProvider`, `GET /bootstrap`).
- **S2 is the single most important FUTURE decision:** a whole-bootstrap publish
endpoint vs. the per-section builder endpoints proposed in §3.8-3.14. The
frontend has an `apiEndpoints.builder` record slot for exactly this but no code
that calls it. **Requires backend decision:** whole-document publish vs.
granular section writes (and optimistic-concurrency / versioning via
`BootstrapConfig.schemaVersion`/`generatedAt`).
- **Headers/JWT:** builder/settings auth. Permission (Proposed): `settings.manage`
(S2/S4), `builder.read`/`settings` read for S1/S3.
- **Validation:** `ProjectValidator` runs client-side before a (currently local)
publish. Backend-side schema validation of the whole `BootstrapConfig` =
Requires backend decision.
- **Errors:** `422` (invalid config), `409` (concurrent edit — proposed, if
versioning adopted), common set.
---
### 3.16 Transactions — PROPOSED (MOCK-ONLY, no seam)
Contract: `AdminTransactionsGateway`
(`src/app/features/admin/transactions/services/admin-transactions-gateway.interface.ts`).
Impl: `AdminTransactionsLocalGateway` (injected concretely by
`AdminTransactionsFacade`). DTO: `AdminTransaction`
(`src/app/features/admin/transactions/models/admin-transaction.model.ts`).
| # | Method | Path (PROPOSED) | Gateway method | Success |
|---|---|---|---|---|
| T1 | GET | `{base}/backoffice/transactions?search&status&type&page&pageSize` | `loadTransactions(filters)` | `200 AdminTransactionsListResult` |
| T2 | POST | `{base}/backoffice/transactions/{id}/retry` | `retryFailed(id)` | `200 AdminTransaction` |
| T3 | PATCH | `{base}/backoffice/transactions/{id}/fraud-flag` | `setFraudFlag(id,flagged)` | `200 AdminTransaction` |
- **Headers/JWT:** admin auth (§3.0). Permission (Proposed): `backoffice.read`
(T1), `backoffice.write` (T2/T3).
- **T1 query params** (`AdminTransactionListFilters`): `search`, `status`
`all|pending|success|failed|retried`, `type``all|payment|refund|qr_payment`,
`page`, `pageSize`. Response `AdminTransactionsListResult { items, total, page,
pageSize }`. No sort param.
- **T2:** only meaningful for `status:'failed'` transactions (method name
`retryFailed`); a backend should reject retry on non-failed (proposed `409`).
- **T3 body:** `{ flagged: boolean }`.
- **`AdminTransaction`:** `{ id, orderId, orderNumber, type, method, status,
amount, currency, fraudFlag, audit: [{ action, actor, timestamp }], createdAt,
updatedAt }`. Transactions link to orders via `orderId` — how they relate to the
live payment records (§3.3.a QR/card) is **Requires backend decision** (the
live payment API and the admin transactions model are currently unconnected).
- **Validation:** none client-side.
- **Errors:** `404` (unknown id), `409` (retry on non-failed — proposed), common
set.
- **Example T3 request:** `PATCH …/transactions/tx_5/fraud-flag` body `{ "flagged": true }`
---
### 3.17 Reviews — storefront submission (LIVE) + admin moderation (MOCK-ONLY)
#### 3.17.a Storefront review/question submission — LIVE
Via `ApiService` (`ProductDataProvider.submitReview/submitQuestion`). Reviews and
questions are **read** by deriving them from the `GET /items/{id}` payload — there
are **no dedicated list endpoints** (`BACKEND-AUDIT.md` §11). Writes:
| # | Method | Path (LIVE literal) | Request | Success |
|---|---|---|---|---|
| R1 | POST | `{base}/items/{itemID}/callback` | `{ rating, comment, sessionID, timestamp }` | `200 { message }` |
| R2 | POST | `{base}/items/{itemID}/questiion` | `{ question, sessionID, timestamp }` | `200 { message }` |
- **Note the literal typo `questiion`** (R2) — matches the backend spec, must be
preserved (`api.service.ts` line 617).
- **Headers/JWT:** marketplace headers; customer `WebSessionID` identifies the
reviewer. No JWT.
- **Models:** `SubmitReviewInput`/`SubmitQuestionInput`
(`src/app/core/products/models/product-engagement.model.ts`). `rating` is
1-5 stars (`RatingStars`).
- **Validation:** none client-side. Rating range 1-5, non-empty comment, one
review per customer/product = Requires backend decision.
- **Errors:** `404` (unknown item), `422` (bad rating), `429` (spam throttle —
proposed, nothing client-side), common set.
- **Example R1 request:** `{ "rating": 5, "comment": "Great", "sessionID": "…", "timestamp": "2026-07-25T12:00:00Z" }`
#### 3.17.b Admin review moderation — PROPOSED (MOCK-ONLY, no seam)
Contract: `AdminModerationGateway`
(`src/app/features/admin/moderation/services/admin-moderation-gateway.interface.ts`).
Impl: `AdminModerationLocalGateway` (injected concretely by
`AdminModerationFacade`; reused by `AdminAnalyticsFacade`). DTO: `AdminReview`
(`src/app/features/admin/moderation/models/admin-review.model.ts`).
| # | Method | Path (PROPOSED) | Gateway method | Success |
|---|---|---|---|---|
| MR1 | GET | `{base}/backoffice/moderation/reviews?search&status&rating&page&pageSize` | `loadReviews(filters)` | `200 AdminReviewsListResult` |
| MR2 | GET | `{base}/backoffice/moderation/reviews/{id}` | `loadReview(id)` | `200 AdminReview` / `404` |
| MR3 | PATCH | `{base}/backoffice/moderation/reviews/{id}/status` | `setReviewStatus(id,status,note)` | `200 AdminReview` |
| MR4 | PATCH | `{base}/backoffice/moderation/reviews/{id}/visible` | `setReviewVisible(id,visible)` | `200 AdminReview` |
| MR5 | PATCH | `{base}/backoffice/moderation/reviews/{id}/pinned` | `setReviewPinned(id,pinned)` | `200 AdminReview` |
| MR6 | PATCH | `{base}/backoffice/moderation/reviews/{id}/featured` | `setReviewFeatured(id,featured)` | `200 AdminReview` |
| MR7 | POST | `{base}/backoffice/moderation/reviews/{id}/notes` | `addModeratorNote(id,note)` | `200 AdminReview` |
| MR8 | DELETE | `{base}/backoffice/moderation/reviews/{id}` | `deleteReview(id)` | `204` |
- **Headers/JWT:** admin auth (§3.0). Permission (Proposed): `backoffice.read`
(MR1/MR2), `backoffice.write` (MR3-MR8).
- **MR1 query params** (`AdminReviewListFilters`): `search`, `status`
`all|pending|approved|rejected|spam`, `rating``all` or a number 1-5, `page`,
`pageSize`. Response `AdminReviewsListResult { items, total, page, pageSize }`.
- **MR3 (approve/reject) body:** `{ status: AdminReviewStatus, note: string }`
where status ∈ `pending|approved|rejected|spam`. This is the approve/reject
action — approving = `status:'approved'`, rejecting = `'rejected'`, marking spam
= `'spam'`. The `note` is a moderator reason, appended to the review timeline.
- **`AdminReview`:** `{ id, productId, productName, customerName, customerEmail,
rating, text, photos[], status, visible, pinned, featured, reportCount,
moderatorNotes, timeline: [{ eventKey: 'submitted'|'statusChanged'|'restored'|
'hidden', actor: 'admin'|'customer', status?, note, timestamp }], createdAt,
updatedAt }`. Server appends timeline entries on each mutation.
- **Relation to storefront reviews (§3.17.a):** the admin `AdminReview` and the
storefront `Review` (`product-engagement.model.ts`) are different types over the
same underlying data. How a `POST /items/{id}/callback` submission surfaces in
the moderation queue = Requires backend decision (they are unconnected today).
- **Validation:** none client-side.
- **Errors:** `404` (unknown id), common set.
- **Example MR3 request:** `{ "status": "approved", "note": "Looks legit" }`
---
### 3.18 Reports — PROPOSED (MOCK-ONLY, no seam)
Part of `AdminModerationGateway` (same gateway/facade as reviews §3.17.b). DTO:
`AdminReport` (`src/app/features/admin/moderation/models/admin-report.model.ts`).
| # | Method | Path (PROPOSED) | Gateway method | Success |
|---|---|---|---|---|
| RP1 | GET | `{base}/backoffice/moderation/reports` | `loadReports()` | `200 AdminReport[]` |
| RP2 | PATCH | `{base}/backoffice/moderation/reports/{id}/status` | `setReportStatus(id,status)` | `200 AdminReport` |
- **Headers/JWT:** admin auth (§3.0). Permission (Proposed): `backoffice.read`
(RP1), `backoffice.write` (RP2).
- **RP1:** returns a **plain array, no pagination/filter params** (`loadReports()`
takes no arguments).
- **RP2 body:** `{ status: AdminReportStatus }` where status ∈
`open|resolved|dismissed`.
- **`AdminReport`:** `{ id, targetType: 'product'|'review'|'customer'|'category'
|'unknown', targetId, targetLabel, reason, reporterEmail, status, createdAt }`.
A report points at another entity via `targetType`+`targetId`.
- **No report *creation* endpoint in the frontend** — reports are read + status
changed only. Who/what creates reports (customer-facing "report this" flow) =
Requires backend decision (no such storefront submission exists in code).
- **Validation:** none client-side. **Errors:** `404` (RP2 unknown id), common set.
- **Example RP2 request:** `{ "status": "resolved" }`
---
### 3.19 Monitoring — PROPOSED, READ-ONLY (MOCK-ONLY, no seam)
Contract: `AdminMonitoringGateway`
(`src/app/features/admin/monitoring/services/admin-monitoring-gateway.interface.ts`).
Impl: `AdminMonitoringLocalGateway` (injected concretely by
`AdminMonitoringFacade`). **Read-only** — the gateway has no mutation methods
(`BACKEND-AUDIT.md` §14). DTOs in `admin-monitoring.model.ts`.
| # | Method | Path (PROPOSED) | Gateway method | Success |
|---|---|---|---|---|
| MO1 | GET | `{base}/backoffice/monitoring/events?category&search` | `loadEvents(filters)` | `200 AdminMonitoringEvent[]` |
| MO2 | GET | `{base}/backoffice/monitoring/queues` | `loadQueues()` | `200 AdminQueue[]` |
| MO3 | GET | `{base}/backoffice/monitoring/webhooks` | `loadWebhooks()` | `200 AdminWebhookDelivery[]` |
- **Headers/JWT:** admin auth (§3.0). Permission (Proposed): `backoffice.read`.
- **MO1 query params** (`AdminMonitoringEventFilters`): `category`
`all|audit|security|login|failed_login|api|error|warning`, `search`. **No
pagination** (plain array) — a backend feeding a real event stream would likely
need to add paging/time-range params; **Requires backend decision** on
windowing.
- **Models:** `AdminMonitoringEvent { id, category, level: 'info'|'warning'
|'error', message, technicalDetail?, actor, timestamp }`; `AdminQueue { name,
depth, status: 'healthy'|'degraded'|'down' }`; `AdminWebhookDelivery { id,
endpoint, event, status: 'delivered'|'failed'|'pending', timestamp }`.
- **Webhooks here are read-only observability** (delivery log), **not** webhook
configuration CRUD — there is no create/update/delete webhook endpoint in the
frontend. Webhook *management* = Requires backend decision (out of current scope).
- **Validation/errors:** read-only; common read error set.
- **Example MO2 response:** `[ { "name": "orders", "depth": 3, "status": "healthy" } ]`
---
### 3.20 Analytics — PROPOSED, DERIVED / NO REAL DATA SOURCE (MOCK-ONLY)
Per `BACKEND-AUDIT.md` §14: `AdminAnalyticsFacade` has **no gateway of its own**
it derives everything by reusing orders/products/moderation local gateways +
`ADMIN_CATEGORIES_GATEWAY` + `AdminDashboardFacade`. Several analytics values are
**honestly `null` because no data source exists** (e.g. `conversionRate`,
`retentionPercent` — model comments: "null = unknown - no visitor/traffic tracking
exists yet. Never fabricated"). DTOs in `admin-analytics.model.ts`.
| # | Method | Path (PROPOSED) | Purpose | Success |
|---|---|---|---|---|
| AN1 | GET | `{base}/backoffice/analytics/summary?range` | KPI summary | `200 AdminAnalyticsSummary` |
| AN2 | GET | `{base}/backoffice/analytics/products?range` | Product analytics | `200 AdminProductAnalytics` |
| AN3 | GET | `{base}/backoffice/analytics/customers?range` | Customer analytics | `200 AdminCustomerAnalytics` |
| AN4 | GET | `{base}/backoffice/analytics/series?range&metric` | Time series | `200 AdminAnalyticsSeriesPoint[]` |
- **Headers/JWT:** admin auth (§3.0). Permission (Proposed): `backoffice.read`.
- **`range` query param** ∈ `7 | 30 | 90` (`AdminAnalyticsDateRange`) — the only
filter the frontend models.
- **`AdminAnalyticsSummary`:** `{ revenueTotal, currency, ordersCount,
avgOrderValue, productsCount, categoriesCount, customersCount, conversionRate:
number | null }`. **`conversionRate` MUST be `null` when unknown** — a backend
must not fabricate it absent real visitor/traffic tracking. Same for
`AdminCustomerAnalytics.retentionPercent`.
- **What has no backend data source at all (mark accordingly):** conversion rate,
retention, and anything needing visitor/traffic/pageview data — there is **no
traffic analytics ingestion anywhere in the frontend**. Building real analytics
(beyond order/product aggregates) is **Requires backend decision** end to end.
- **Validation/errors:** read-only; common read set. A backend that can't compute
a metric should return `null`, not omit or fake it.
- **Example AN1 response:**
```json
{ "revenueTotal": 4820000, "currency": "RUB", "ordersCount": 213, "avgOrderValue": 22629, "productsCount": 512, "categoriesCount": 24, "customersCount": 178, "conversionRate": null }
```
**Dashboard metrics (adjacent).** `AdminDashboardMetricsGateway.loadMetrics()`
(`ADMIN_DASHBOARD_METRICS_GATEWAY`, MOCK-SWAPPABLE token) returns
`AdminDashboardMetrics { categoriesCount, productsCount }` — the one other
token-bound admin gateway. Proposed: `GET {base}/backoffice/dashboard/metrics`
`200 { categoriesCount, productsCount }`, permission `backoffice.read`.
---
### 3.21 Cross-cutting "Requires backend decision" register (this section)
docs: finalize BACKEND.md as a self-contained implementation prompt Goal: this document alone, pasted into a fresh Claude session, should be enough for a backend dev to implement against without getting stuck or inventing conventions the frontend doesn't actually need. Fixed a real bug: ~65 cross-references throughout the document pointed to docs/AUTHENTICATION.md, docs/ERROR_CONTRACT.md, docs/MAINTENANCE_MODE.md - three sibling docs that were deleted and fully merged into this document's own §4/§6/§10 during the earlier doc-consolidation pass, but the in-text references were never updated. A fresh agent following those links would hit dead ends repeatedly. Bulk-replaced with in- document section references; hand-fixed ~4 sentences that framed §6/ §10 as "sibling task, in progress" (stale - both are complete, this document's §6/§10 already are the settled contract, nothing to wait on). Added "Recommended default" to every item across the document's 5 consolidated "Requires backend decision" registers (§2.12 framework, §3.21 CRUD cross-cutting - 18 items, §4 §12 auth open items - 9 items, §6 error-model summary - 9 items, §10 maintenance-mode list - 5 items). Each default is derived from what the frontend already implies or standard REST/security convention - no invented APIs or business rules. ~6 items are explicitly flagged as real business/security decisions instead (order state-machine rules, Ed25519 cutover strategy, refresh-token reuse-detection posture) since those carry consequences no amount of frontend-code-reading can resolve. Added a "How to use this document" preamble up front: work in §9's dependency order not document order, apply recommended defaults and keep moving, only stop for the explicitly-flagged business/security items, don't invent beyond what's written or directly implied. Frontend side: confirmed nothing else is left. docs/TODO.md already has zero blockers; docs/KNOWN-ISSUES.md's one open item (unreachable Ed25519 error-code UI) is correctly left open and documented rather than faked closed - fixing it needs a real backend emitting real distinguishable error codes, which doesn't exist yet and can't be fabricated without inventing an API contract. No frontend code touched. No architecture changed. No APIs invented.
2026-07-26 16:10:33 +04:00
Consolidated so the master doc can dedupe against the auth/error registers.
Each item has a **Recommended default** — apply it unless you have a specific
reason not to; only stop and ask a human where none is given or the item has
real business/money/legal weight (marked accordingly).
1. **DI seam prerequisite** — orders, products, users, transactions, monitoring,
moderation (+ derived customers/analytics) inject their `*LocalGateway`
concretely; a backend requires introducing a DI token first (`BACKEND-AUDIT.md`
§14). Only categories + dashboard-metrics are token-bound today.
docs: finalize BACKEND.md as a self-contained implementation prompt Goal: this document alone, pasted into a fresh Claude session, should be enough for a backend dev to implement against without getting stuck or inventing conventions the frontend doesn't actually need. Fixed a real bug: ~65 cross-references throughout the document pointed to docs/AUTHENTICATION.md, docs/ERROR_CONTRACT.md, docs/MAINTENANCE_MODE.md - three sibling docs that were deleted and fully merged into this document's own §4/§6/§10 during the earlier doc-consolidation pass, but the in-text references were never updated. A fresh agent following those links would hit dead ends repeatedly. Bulk-replaced with in- document section references; hand-fixed ~4 sentences that framed §6/ §10 as "sibling task, in progress" (stale - both are complete, this document's §6/§10 already are the settled contract, nothing to wait on). Added "Recommended default" to every item across the document's 5 consolidated "Requires backend decision" registers (§2.12 framework, §3.21 CRUD cross-cutting - 18 items, §4 §12 auth open items - 9 items, §6 error-model summary - 9 items, §10 maintenance-mode list - 5 items). Each default is derived from what the frontend already implies or standard REST/security convention - no invented APIs or business rules. ~6 items are explicitly flagged as real business/security decisions instead (order state-machine rules, Ed25519 cutover strategy, refresh-token reuse-detection posture) since those carry consequences no amount of frontend-code-reading can resolve. Added a "How to use this document" preamble up front: work in §9's dependency order not document order, apply recommended defaults and keep moving, only stop for the explicitly-flagged business/security items, don't invent beyond what's written or directly implied. Frontend side: confirmed nothing else is left. docs/TODO.md already has zero blockers; docs/KNOWN-ISSUES.md's one open item (unreachable Ed25519 error-code UI) is correctly left open and documented rather than faked closed - fixing it needs a real backend emitting real distinguishable error codes, which doesn't exist yet and can't be fabricated without inventing an API contract. No frontend code touched. No architecture changed. No APIs invented.
2026-07-26 16:10:33 +04:00
**Recommended default:** this is frontend work (adding the token seam), do
it for every domain before wiring its real gateway — mechanical, no backend
input needed. See §8.5 for the exact per-domain pattern.
2. **All admin CRUD paths are PROPOSED** — only `/backoffice/categories*` and the
storefront catalog/cart/engagement paths are literal. Adopt the
`/backoffice/<domain>` convention (or reject it).
docs: finalize BACKEND.md as a self-contained implementation prompt Goal: this document alone, pasted into a fresh Claude session, should be enough for a backend dev to implement against without getting stuck or inventing conventions the frontend doesn't actually need. Fixed a real bug: ~65 cross-references throughout the document pointed to docs/AUTHENTICATION.md, docs/ERROR_CONTRACT.md, docs/MAINTENANCE_MODE.md - three sibling docs that were deleted and fully merged into this document's own §4/§6/§10 during the earlier doc-consolidation pass, but the in-text references were never updated. A fresh agent following those links would hit dead ends repeatedly. Bulk-replaced with in- document section references; hand-fixed ~4 sentences that framed §6/ §10 as "sibling task, in progress" (stale - both are complete, this document's §6/§10 already are the settled contract, nothing to wait on). Added "Recommended default" to every item across the document's 5 consolidated "Requires backend decision" registers (§2.12 framework, §3.21 CRUD cross-cutting - 18 items, §4 §12 auth open items - 9 items, §6 error-model summary - 9 items, §10 maintenance-mode list - 5 items). Each default is derived from what the frontend already implies or standard REST/security convention - no invented APIs or business rules. ~6 items are explicitly flagged as real business/security decisions instead (order state-machine rules, Ed25519 cutover strategy, refresh-token reuse-detection posture) since those carry consequences no amount of frontend-code-reading can resolve. Added a "How to use this document" preamble up front: work in §9's dependency order not document order, apply recommended defaults and keep moving, only stop for the explicitly-flagged business/security items, don't invent beyond what's written or directly implied. Frontend side: confirmed nothing else is left. docs/TODO.md already has zero blockers; docs/KNOWN-ISSUES.md's one open item (unreachable Ed25519 error-code UI) is correctly left open and documented rather than faked closed - fixing it needs a real backend emitting real distinguishable error codes, which doesn't exist yet and can't be fabricated without inventing an API contract. No frontend code touched. No architecture changed. No APIs invented.
2026-07-26 16:10:33 +04:00
**Recommended default:** adopt it as-is — it's already live for categories,
changing it later is a bigger cost than any alternative naming would save.
3. **Server-owned field lists** on create/update for products/orders/etc. (only
categories defines one, by stripping `id/itemsCount/deletedAt/createdAt/updatedAt`).
docs: finalize BACKEND.md as a self-contained implementation prompt Goal: this document alone, pasted into a fresh Claude session, should be enough for a backend dev to implement against without getting stuck or inventing conventions the frontend doesn't actually need. Fixed a real bug: ~65 cross-references throughout the document pointed to docs/AUTHENTICATION.md, docs/ERROR_CONTRACT.md, docs/MAINTENANCE_MODE.md - three sibling docs that were deleted and fully merged into this document's own §4/§6/§10 during the earlier doc-consolidation pass, but the in-text references were never updated. A fresh agent following those links would hit dead ends repeatedly. Bulk-replaced with in- document section references; hand-fixed ~4 sentences that framed §6/ §10 as "sibling task, in progress" (stale - both are complete, this document's §6/§10 already are the settled contract, nothing to wait on). Added "Recommended default" to every item across the document's 5 consolidated "Requires backend decision" registers (§2.12 framework, §3.21 CRUD cross-cutting - 18 items, §4 §12 auth open items - 9 items, §6 error-model summary - 9 items, §10 maintenance-mode list - 5 items). Each default is derived from what the frontend already implies or standard REST/security convention - no invented APIs or business rules. ~6 items are explicitly flagged as real business/security decisions instead (order state-machine rules, Ed25519 cutover strategy, refresh-token reuse-detection posture) since those carry consequences no amount of frontend-code-reading can resolve. Added a "How to use this document" preamble up front: work in §9's dependency order not document order, apply recommended defaults and keep moving, only stop for the explicitly-flagged business/security items, don't invent beyond what's written or directly implied. Frontend side: confirmed nothing else is left. docs/TODO.md already has zero blockers; docs/KNOWN-ISSUES.md's one open item (unreachable Ed25519 error-code UI) is correctly left open and documented rather than faked closed - fixing it needs a real backend emitting real distinguishable error codes, which doesn't exist yet and can't be fabricated without inventing an API contract. No frontend code touched. No architecture changed. No APIs invented.
2026-07-26 16:10:33 +04:00
**Recommended default:** mirror the categories pattern exactly (server strips
`id`/counts/timestamps from client payloads; client never sends them).
4. **Field validation + error wording** — the frontend enforces essentially none
on admin CRUD (no reactive `Validators`); all `422` field rules and messages are
backend-owned.
docs: finalize BACKEND.md as a self-contained implementation prompt Goal: this document alone, pasted into a fresh Claude session, should be enough for a backend dev to implement against without getting stuck or inventing conventions the frontend doesn't actually need. Fixed a real bug: ~65 cross-references throughout the document pointed to docs/AUTHENTICATION.md, docs/ERROR_CONTRACT.md, docs/MAINTENANCE_MODE.md - three sibling docs that were deleted and fully merged into this document's own §4/§6/§10 during the earlier doc-consolidation pass, but the in-text references were never updated. A fresh agent following those links would hit dead ends repeatedly. Bulk-replaced with in- document section references; hand-fixed ~4 sentences that framed §6/ §10 as "sibling task, in progress" (stale - both are complete, this document's §6/§10 already are the settled contract, nothing to wait on). Added "Recommended default" to every item across the document's 5 consolidated "Requires backend decision" registers (§2.12 framework, §3.21 CRUD cross-cutting - 18 items, §4 §12 auth open items - 9 items, §6 error-model summary - 9 items, §10 maintenance-mode list - 5 items). Each default is derived from what the frontend already implies or standard REST/security convention - no invented APIs or business rules. ~6 items are explicitly flagged as real business/security decisions instead (order state-machine rules, Ed25519 cutover strategy, refresh-token reuse-detection posture) since those carry consequences no amount of frontend-code-reading can resolve. Added a "How to use this document" preamble up front: work in §9's dependency order not document order, apply recommended defaults and keep moving, only stop for the explicitly-flagged business/security items, don't invent beyond what's written or directly implied. Frontend side: confirmed nothing else is left. docs/TODO.md already has zero blockers; docs/KNOWN-ISSUES.md's one open item (unreachable Ed25519 error-code UI) is correctly left open and documented rather than faked closed - fixing it needs a real backend emitting real distinguishable error codes, which doesn't exist yet and can't be fabricated without inventing an API contract. No frontend code touched. No architecture changed. No APIs invented.
2026-07-26 16:10:33 +04:00
**Recommended default:** validate required/type/length server-side per the DTO
shapes given in §3; return field errors in the §6 `422` shape (`fieldErrors`
keyed by field path). Don't wait on frontend validation to be added first.
5. **Product slug/sku uniqueness** — no proactive check exists (unlike categories'
`isSlugTaken`); decide `409` behavior.
docs: finalize BACKEND.md as a self-contained implementation prompt Goal: this document alone, pasted into a fresh Claude session, should be enough for a backend dev to implement against without getting stuck or inventing conventions the frontend doesn't actually need. Fixed a real bug: ~65 cross-references throughout the document pointed to docs/AUTHENTICATION.md, docs/ERROR_CONTRACT.md, docs/MAINTENANCE_MODE.md - three sibling docs that were deleted and fully merged into this document's own §4/§6/§10 during the earlier doc-consolidation pass, but the in-text references were never updated. A fresh agent following those links would hit dead ends repeatedly. Bulk-replaced with in- document section references; hand-fixed ~4 sentences that framed §6/ §10 as "sibling task, in progress" (stale - both are complete, this document's §6/§10 already are the settled contract, nothing to wait on). Added "Recommended default" to every item across the document's 5 consolidated "Requires backend decision" registers (§2.12 framework, §3.21 CRUD cross-cutting - 18 items, §4 §12 auth open items - 9 items, §6 error-model summary - 9 items, §10 maintenance-mode list - 5 items). Each default is derived from what the frontend already implies or standard REST/security convention - no invented APIs or business rules. ~6 items are explicitly flagged as real business/security decisions instead (order state-machine rules, Ed25519 cutover strategy, refresh-token reuse-detection posture) since those carry consequences no amount of frontend-code-reading can resolve. Added a "How to use this document" preamble up front: work in §9's dependency order not document order, apply recommended defaults and keep moving, only stop for the explicitly-flagged business/security items, don't invent beyond what's written or directly implied. Frontend side: confirmed nothing else is left. docs/TODO.md already has zero blockers; docs/KNOWN-ISSUES.md's one open item (unreachable Ed25519 error-code UI) is correctly left open and documented rather than faked closed - fixing it needs a real backend emitting real distinguishable error codes, which doesn't exist yet and can't be fabricated without inventing an API contract. No frontend code touched. No architecture changed. No APIs invented.
2026-07-26 16:10:33 +04:00
**Recommended default:** mirror categories — add an `isSlugTaken`-equivalent
check endpoint, and return `409` with the §6 conflict shape on collision.
6. **Order status-transition legality** and refund eligibility rules.
docs: finalize BACKEND.md as a self-contained implementation prompt Goal: this document alone, pasted into a fresh Claude session, should be enough for a backend dev to implement against without getting stuck or inventing conventions the frontend doesn't actually need. Fixed a real bug: ~65 cross-references throughout the document pointed to docs/AUTHENTICATION.md, docs/ERROR_CONTRACT.md, docs/MAINTENANCE_MODE.md - three sibling docs that were deleted and fully merged into this document's own §4/§6/§10 during the earlier doc-consolidation pass, but the in-text references were never updated. A fresh agent following those links would hit dead ends repeatedly. Bulk-replaced with in- document section references; hand-fixed ~4 sentences that framed §6/ §10 as "sibling task, in progress" (stale - both are complete, this document's §6/§10 already are the settled contract, nothing to wait on). Added "Recommended default" to every item across the document's 5 consolidated "Requires backend decision" registers (§2.12 framework, §3.21 CRUD cross-cutting - 18 items, §4 §12 auth open items - 9 items, §6 error-model summary - 9 items, §10 maintenance-mode list - 5 items). Each default is derived from what the frontend already implies or standard REST/security convention - no invented APIs or business rules. ~6 items are explicitly flagged as real business/security decisions instead (order state-machine rules, Ed25519 cutover strategy, refresh-token reuse-detection posture) since those carry consequences no amount of frontend-code-reading can resolve. Added a "How to use this document" preamble up front: work in §9's dependency order not document order, apply recommended defaults and keep moving, only stop for the explicitly-flagged business/security items, don't invent beyond what's written or directly implied. Frontend side: confirmed nothing else is left. docs/TODO.md already has zero blockers; docs/KNOWN-ISSUES.md's one open item (unreachable Ed25519 error-code UI) is correctly left open and documented rather than faked closed - fixing it needs a real backend emitting real distinguishable error codes, which doesn't exist yet and can't be fabricated without inventing an API contract. No frontend code touched. No architecture changed. No APIs invented.
2026-07-26 16:10:33 +04:00
**Business decision, not technical** — the valid state machine and who can
trigger which transition depends on the merchant's actual fulfillment
process. Ask.
7. **Customers as a first-class resource** vs. derived-from-orders aggregate.
docs: finalize BACKEND.md as a self-contained implementation prompt Goal: this document alone, pasted into a fresh Claude session, should be enough for a backend dev to implement against without getting stuck or inventing conventions the frontend doesn't actually need. Fixed a real bug: ~65 cross-references throughout the document pointed to docs/AUTHENTICATION.md, docs/ERROR_CONTRACT.md, docs/MAINTENANCE_MODE.md - three sibling docs that were deleted and fully merged into this document's own §4/§6/§10 during the earlier doc-consolidation pass, but the in-text references were never updated. A fresh agent following those links would hit dead ends repeatedly. Bulk-replaced with in- document section references; hand-fixed ~4 sentences that framed §6/ §10 as "sibling task, in progress" (stale - both are complete, this document's §6/§10 already are the settled contract, nothing to wait on). Added "Recommended default" to every item across the document's 5 consolidated "Requires backend decision" registers (§2.12 framework, §3.21 CRUD cross-cutting - 18 items, §4 §12 auth open items - 9 items, §6 error-model summary - 9 items, §10 maintenance-mode list - 5 items). Each default is derived from what the frontend already implies or standard REST/security convention - no invented APIs or business rules. ~6 items are explicitly flagged as real business/security decisions instead (order state-machine rules, Ed25519 cutover strategy, refresh-token reuse-detection posture) since those carry consequences no amount of frontend-code-reading can resolve. Added a "How to use this document" preamble up front: work in §9's dependency order not document order, apply recommended defaults and keep moving, only stop for the explicitly-flagged business/security items, don't invent beyond what's written or directly implied. Frontend side: confirmed nothing else is left. docs/TODO.md already has zero blockers; docs/KNOWN-ISSUES.md's one open item (unreachable Ed25519 error-code UI) is correctly left open and documented rather than faked closed - fixing it needs a real backend emitting real distinguishable error codes, which doesn't exist yet and can't be fabricated without inventing an API contract. No frontend code touched. No architecture changed. No APIs invented.
2026-07-26 16:10:33 +04:00
**Recommended default:** derived-from-orders aggregate (§3.4 as written) —
it's what the frontend already assumes and needs no new storage model. Only
promote to first-class if a real need appears (e.g. customer accounts,
guest-order merging).
8. **`AdminRole` duplication** — auth string-union vs. users-management interface
(naming reconciliation; role CRUD / custom roles).
docs: finalize BACKEND.md as a self-contained implementation prompt Goal: this document alone, pasted into a fresh Claude session, should be enough for a backend dev to implement against without getting stuck or inventing conventions the frontend doesn't actually need. Fixed a real bug: ~65 cross-references throughout the document pointed to docs/AUTHENTICATION.md, docs/ERROR_CONTRACT.md, docs/MAINTENANCE_MODE.md - three sibling docs that were deleted and fully merged into this document's own §4/§6/§10 during the earlier doc-consolidation pass, but the in-text references were never updated. A fresh agent following those links would hit dead ends repeatedly. Bulk-replaced with in- document section references; hand-fixed ~4 sentences that framed §6/ §10 as "sibling task, in progress" (stale - both are complete, this document's §6/§10 already are the settled contract, nothing to wait on). Added "Recommended default" to every item across the document's 5 consolidated "Requires backend decision" registers (§2.12 framework, §3.21 CRUD cross-cutting - 18 items, §4 §12 auth open items - 9 items, §6 error-model summary - 9 items, §10 maintenance-mode list - 5 items). Each default is derived from what the frontend already implies or standard REST/security convention - no invented APIs or business rules. ~6 items are explicitly flagged as real business/security decisions instead (order state-machine rules, Ed25519 cutover strategy, refresh-token reuse-detection posture) since those carry consequences no amount of frontend-code-reading can resolve. Added a "How to use this document" preamble up front: work in §9's dependency order not document order, apply recommended defaults and keep moving, only stop for the explicitly-flagged business/security items, don't invent beyond what's written or directly implied. Frontend side: confirmed nothing else is left. docs/TODO.md already has zero blockers; docs/KNOWN-ISSUES.md's one open item (unreachable Ed25519 error-code UI) is correctly left open and documented rather than faked closed - fixing it needs a real backend emitting real distinguishable error codes, which doesn't exist yet and can't be fabricated without inventing an API contract. No frontend code touched. No architecture changed. No APIs invented.
2026-07-26 16:10:33 +04:00
**Recommended default:** keep the coarse `AdminRole` string union (§4 §9.1)
as the enforced permission source; treat the users-management interface's
richer shape as display-only until custom roles are actually requested by
the product.
9. **Remote session revocation propagation** (`revokeSession`) to a logged-in
docs: finalize BACKEND.md as a self-contained implementation prompt Goal: this document alone, pasted into a fresh Claude session, should be enough for a backend dev to implement against without getting stuck or inventing conventions the frontend doesn't actually need. Fixed a real bug: ~65 cross-references throughout the document pointed to docs/AUTHENTICATION.md, docs/ERROR_CONTRACT.md, docs/MAINTENANCE_MODE.md - three sibling docs that were deleted and fully merged into this document's own §4/§6/§10 during the earlier doc-consolidation pass, but the in-text references were never updated. A fresh agent following those links would hit dead ends repeatedly. Bulk-replaced with in- document section references; hand-fixed ~4 sentences that framed §6/ §10 as "sibling task, in progress" (stale - both are complete, this document's §6/§10 already are the settled contract, nothing to wait on). Added "Recommended default" to every item across the document's 5 consolidated "Requires backend decision" registers (§2.12 framework, §3.21 CRUD cross-cutting - 18 items, §4 §12 auth open items - 9 items, §6 error-model summary - 9 items, §10 maintenance-mode list - 5 items). Each default is derived from what the frontend already implies or standard REST/security convention - no invented APIs or business rules. ~6 items are explicitly flagged as real business/security decisions instead (order state-machine rules, Ed25519 cutover strategy, refresh-token reuse-detection posture) since those carry consequences no amount of frontend-code-reading can resolve. Added a "How to use this document" preamble up front: work in §9's dependency order not document order, apply recommended defaults and keep moving, only stop for the explicitly-flagged business/security items, don't invent beyond what's written or directly implied. Frontend side: confirmed nothing else is left. docs/TODO.md already has zero blockers; docs/KNOWN-ISSUES.md's one open item (unreachable Ed25519 error-code UI) is correctly left open and documented rather than faked closed - fixing it needs a real backend emitting real distinguishable error codes, which doesn't exist yet and can't be fabricated without inventing an API contract. No frontend code touched. No architecture changed. No APIs invented.
2026-07-26 16:10:33 +04:00
client (`§4` §8 — no push/poll exists).
**Recommended default:** accept the propagation delay (revoked session stays
client-usable until its next natural refresh-interval tick, §4 §8) rather
than building push/poll infrastructure for this alone — revisit only if a
real incident makes the delay unacceptable.
10. **Media**: allowed mime/size; whether in-use assets are delete-blocked.
docs: finalize BACKEND.md as a self-contained implementation prompt Goal: this document alone, pasted into a fresh Claude session, should be enough for a backend dev to implement against without getting stuck or inventing conventions the frontend doesn't actually need. Fixed a real bug: ~65 cross-references throughout the document pointed to docs/AUTHENTICATION.md, docs/ERROR_CONTRACT.md, docs/MAINTENANCE_MODE.md - three sibling docs that were deleted and fully merged into this document's own §4/§6/§10 during the earlier doc-consolidation pass, but the in-text references were never updated. A fresh agent following those links would hit dead ends repeatedly. Bulk-replaced with in- document section references; hand-fixed ~4 sentences that framed §6/ §10 as "sibling task, in progress" (stale - both are complete, this document's §6/§10 already are the settled contract, nothing to wait on). Added "Recommended default" to every item across the document's 5 consolidated "Requires backend decision" registers (§2.12 framework, §3.21 CRUD cross-cutting - 18 items, §4 §12 auth open items - 9 items, §6 error-model summary - 9 items, §10 maintenance-mode list - 5 items). Each default is derived from what the frontend already implies or standard REST/security convention - no invented APIs or business rules. ~6 items are explicitly flagged as real business/security decisions instead (order state-machine rules, Ed25519 cutover strategy, refresh-token reuse-detection posture) since those carry consequences no amount of frontend-code-reading can resolve. Added a "How to use this document" preamble up front: work in §9's dependency order not document order, apply recommended defaults and keep moving, only stop for the explicitly-flagged business/security items, don't invent beyond what's written or directly implied. Frontend side: confirmed nothing else is left. docs/TODO.md already has zero blockers; docs/KNOWN-ISSUES.md's one open item (unreachable Ed25519 error-code UI) is correctly left open and documented rather than faked closed - fixing it needs a real backend emitting real distinguishable error codes, which doesn't exist yet and can't be fabricated without inventing an API contract. No frontend code touched. No architecture changed. No APIs invented.
2026-07-26 16:10:33 +04:00
**Recommended default:** allow the mime types the client already filters
for (`image/*`, `application/pdf`, `image/svg+xml` — §7.1 `MediaAssetKind`),
10MB per file; block delete of in-use assets with a `409` listing the
referencing entities (safer default than silent broken references).
11. **CMS/homepage/widgets/nav/footer/branding/languages/settings are LOCAL-ONLY**
— the single biggest decision is **whole-bootstrap publish (S2) vs. granular
builder endpoints** (§3.15). No builder write call exists in code today.
docs: finalize BACKEND.md as a self-contained implementation prompt Goal: this document alone, pasted into a fresh Claude session, should be enough for a backend dev to implement against without getting stuck or inventing conventions the frontend doesn't actually need. Fixed a real bug: ~65 cross-references throughout the document pointed to docs/AUTHENTICATION.md, docs/ERROR_CONTRACT.md, docs/MAINTENANCE_MODE.md - three sibling docs that were deleted and fully merged into this document's own §4/§6/§10 during the earlier doc-consolidation pass, but the in-text references were never updated. A fresh agent following those links would hit dead ends repeatedly. Bulk-replaced with in- document section references; hand-fixed ~4 sentences that framed §6/ §10 as "sibling task, in progress" (stale - both are complete, this document's §6/§10 already are the settled contract, nothing to wait on). Added "Recommended default" to every item across the document's 5 consolidated "Requires backend decision" registers (§2.12 framework, §3.21 CRUD cross-cutting - 18 items, §4 §12 auth open items - 9 items, §6 error-model summary - 9 items, §10 maintenance-mode list - 5 items). Each default is derived from what the frontend already implies or standard REST/security convention - no invented APIs or business rules. ~6 items are explicitly flagged as real business/security decisions instead (order state-machine rules, Ed25519 cutover strategy, refresh-token reuse-detection posture) since those carry consequences no amount of frontend-code-reading can resolve. Added a "How to use this document" preamble up front: work in §9's dependency order not document order, apply recommended defaults and keep moving, only stop for the explicitly-flagged business/security items, don't invent beyond what's written or directly implied. Frontend side: confirmed nothing else is left. docs/TODO.md already has zero blockers; docs/KNOWN-ISSUES.md's one open item (unreachable Ed25519 error-code UI) is correctly left open and documented rather than faked closed - fixing it needs a real backend emitting real distinguishable error codes, which doesn't exist yet and can't be fabricated without inventing an API contract. No frontend code touched. No architecture changed. No APIs invented.
2026-07-26 16:10:33 +04:00
**Recommended default:** whole-bootstrap publish (a single `PUT`/`POST` that
replaces the tenant's bootstrap document) — matches how the frontend already
edits it as one in-memory draft object; granular per-section endpoints are
strictly more backend work for no frontend benefit today.
12. **Languages**: duplicate-add `409`, removing the default/last locale, BCP-47
validation.
docs: finalize BACKEND.md as a self-contained implementation prompt Goal: this document alone, pasted into a fresh Claude session, should be enough for a backend dev to implement against without getting stuck or inventing conventions the frontend doesn't actually need. Fixed a real bug: ~65 cross-references throughout the document pointed to docs/AUTHENTICATION.md, docs/ERROR_CONTRACT.md, docs/MAINTENANCE_MODE.md - three sibling docs that were deleted and fully merged into this document's own §4/§6/§10 during the earlier doc-consolidation pass, but the in-text references were never updated. A fresh agent following those links would hit dead ends repeatedly. Bulk-replaced with in- document section references; hand-fixed ~4 sentences that framed §6/ §10 as "sibling task, in progress" (stale - both are complete, this document's §6/§10 already are the settled contract, nothing to wait on). Added "Recommended default" to every item across the document's 5 consolidated "Requires backend decision" registers (§2.12 framework, §3.21 CRUD cross-cutting - 18 items, §4 §12 auth open items - 9 items, §6 error-model summary - 9 items, §10 maintenance-mode list - 5 items). Each default is derived from what the frontend already implies or standard REST/security convention - no invented APIs or business rules. ~6 items are explicitly flagged as real business/security decisions instead (order state-machine rules, Ed25519 cutover strategy, refresh-token reuse-detection posture) since those carry consequences no amount of frontend-code-reading can resolve. Added a "How to use this document" preamble up front: work in §9's dependency order not document order, apply recommended defaults and keep moving, only stop for the explicitly-flagged business/security items, don't invent beyond what's written or directly implied. Frontend side: confirmed nothing else is left. docs/TODO.md already has zero blockers; docs/KNOWN-ISSUES.md's one open item (unreachable Ed25519 error-code UI) is correctly left open and documented rather than faked closed - fixing it needs a real backend emitting real distinguishable error codes, which doesn't exist yet and can't be fabricated without inventing an API contract. No frontend code touched. No architecture changed. No APIs invented.
2026-07-26 16:10:33 +04:00
**Recommended default:** `409` on duplicate-add; reject removing the last
remaining locale or the current default (`422`); validate against BCP-47
syntax, not a fixed enum (frontend already treats locale as an open string).
13. **Transactions ↔ live payment records** are unconnected models; how they link.
docs: finalize BACKEND.md as a self-contained implementation prompt Goal: this document alone, pasted into a fresh Claude session, should be enough for a backend dev to implement against without getting stuck or inventing conventions the frontend doesn't actually need. Fixed a real bug: ~65 cross-references throughout the document pointed to docs/AUTHENTICATION.md, docs/ERROR_CONTRACT.md, docs/MAINTENANCE_MODE.md - three sibling docs that were deleted and fully merged into this document's own §4/§6/§10 during the earlier doc-consolidation pass, but the in-text references were never updated. A fresh agent following those links would hit dead ends repeatedly. Bulk-replaced with in- document section references; hand-fixed ~4 sentences that framed §6/ §10 as "sibling task, in progress" (stale - both are complete, this document's §6/§10 already are the settled contract, nothing to wait on). Added "Recommended default" to every item across the document's 5 consolidated "Requires backend decision" registers (§2.12 framework, §3.21 CRUD cross-cutting - 18 items, §4 §12 auth open items - 9 items, §6 error-model summary - 9 items, §10 maintenance-mode list - 5 items). Each default is derived from what the frontend already implies or standard REST/security convention - no invented APIs or business rules. ~6 items are explicitly flagged as real business/security decisions instead (order state-machine rules, Ed25519 cutover strategy, refresh-token reuse-detection posture) since those carry consequences no amount of frontend-code-reading can resolve. Added a "How to use this document" preamble up front: work in §9's dependency order not document order, apply recommended defaults and keep moving, only stop for the explicitly-flagged business/security items, don't invent beyond what's written or directly implied. Frontend side: confirmed nothing else is left. docs/TODO.md already has zero blockers; docs/KNOWN-ISSUES.md's one open item (unreachable Ed25519 error-code UI) is correctly left open and documented rather than faked closed - fixing it needs a real backend emitting real distinguishable error codes, which doesn't exist yet and can't be fabricated without inventing an API contract. No frontend code touched. No architecture changed. No APIs invented.
2026-07-26 16:10:33 +04:00
**Recommended default:** key `AdminTransaction` off the same order/payment id
the live QR/card flow already returns (§3.3.a) rather than inventing a new
identifier — one payment, one transaction record.
14. **Reviews**: storefront `Review` vs. admin `AdminReview` linkage; how a
submitted review enters the moderation queue.
docs: finalize BACKEND.md as a self-contained implementation prompt Goal: this document alone, pasted into a fresh Claude session, should be enough for a backend dev to implement against without getting stuck or inventing conventions the frontend doesn't actually need. Fixed a real bug: ~65 cross-references throughout the document pointed to docs/AUTHENTICATION.md, docs/ERROR_CONTRACT.md, docs/MAINTENANCE_MODE.md - three sibling docs that were deleted and fully merged into this document's own §4/§6/§10 during the earlier doc-consolidation pass, but the in-text references were never updated. A fresh agent following those links would hit dead ends repeatedly. Bulk-replaced with in- document section references; hand-fixed ~4 sentences that framed §6/ §10 as "sibling task, in progress" (stale - both are complete, this document's §6/§10 already are the settled contract, nothing to wait on). Added "Recommended default" to every item across the document's 5 consolidated "Requires backend decision" registers (§2.12 framework, §3.21 CRUD cross-cutting - 18 items, §4 §12 auth open items - 9 items, §6 error-model summary - 9 items, §10 maintenance-mode list - 5 items). Each default is derived from what the frontend already implies or standard REST/security convention - no invented APIs or business rules. ~6 items are explicitly flagged as real business/security decisions instead (order state-machine rules, Ed25519 cutover strategy, refresh-token reuse-detection posture) since those carry consequences no amount of frontend-code-reading can resolve. Added a "How to use this document" preamble up front: work in §9's dependency order not document order, apply recommended defaults and keep moving, only stop for the explicitly-flagged business/security items, don't invent beyond what's written or directly implied. Frontend side: confirmed nothing else is left. docs/TODO.md already has zero blockers; docs/KNOWN-ISSUES.md's one open item (unreachable Ed25519 error-code UI) is correctly left open and documented rather than faked closed - fixing it needs a real backend emitting real distinguishable error codes, which doesn't exist yet and can't be fabricated without inventing an API contract. No frontend code touched. No architecture changed. No APIs invented.
2026-07-26 16:10:33 +04:00
**Recommended default:** every storefront-submitted review starts in the
moderation queue at `pending` status; `AdminReview` is the same underlying
record with moderation fields attached, not a separate entity.
15. **Reports**: no creation flow exists — who creates reports.
docs: finalize BACKEND.md as a self-contained implementation prompt Goal: this document alone, pasted into a fresh Claude session, should be enough for a backend dev to implement against without getting stuck or inventing conventions the frontend doesn't actually need. Fixed a real bug: ~65 cross-references throughout the document pointed to docs/AUTHENTICATION.md, docs/ERROR_CONTRACT.md, docs/MAINTENANCE_MODE.md - three sibling docs that were deleted and fully merged into this document's own §4/§6/§10 during the earlier doc-consolidation pass, but the in-text references were never updated. A fresh agent following those links would hit dead ends repeatedly. Bulk-replaced with in- document section references; hand-fixed ~4 sentences that framed §6/ §10 as "sibling task, in progress" (stale - both are complete, this document's §6/§10 already are the settled contract, nothing to wait on). Added "Recommended default" to every item across the document's 5 consolidated "Requires backend decision" registers (§2.12 framework, §3.21 CRUD cross-cutting - 18 items, §4 §12 auth open items - 9 items, §6 error-model summary - 9 items, §10 maintenance-mode list - 5 items). Each default is derived from what the frontend already implies or standard REST/security convention - no invented APIs or business rules. ~6 items are explicitly flagged as real business/security decisions instead (order state-machine rules, Ed25519 cutover strategy, refresh-token reuse-detection posture) since those carry consequences no amount of frontend-code-reading can resolve. Added a "How to use this document" preamble up front: work in §9's dependency order not document order, apply recommended defaults and keep moving, only stop for the explicitly-flagged business/security items, don't invent beyond what's written or directly implied. Frontend side: confirmed nothing else is left. docs/TODO.md already has zero blockers; docs/KNOWN-ISSUES.md's one open item (unreachable Ed25519 error-code UI) is correctly left open and documented rather than faked closed - fixing it needs a real backend emitting real distinguishable error codes, which doesn't exist yet and can't be fabricated without inventing an API contract. No frontend code touched. No architecture changed. No APIs invented.
2026-07-26 16:10:33 +04:00
**Business decision** — depends on whether "reports" means customer-flagged
content, automated abuse detection, or admin-initiated audits. Ask.
16. **Monitoring**: event windowing/pagination for a real stream; webhook
*management* (vs. read-only delivery log) is out of scope.
docs: finalize BACKEND.md as a self-contained implementation prompt Goal: this document alone, pasted into a fresh Claude session, should be enough for a backend dev to implement against without getting stuck or inventing conventions the frontend doesn't actually need. Fixed a real bug: ~65 cross-references throughout the document pointed to docs/AUTHENTICATION.md, docs/ERROR_CONTRACT.md, docs/MAINTENANCE_MODE.md - three sibling docs that were deleted and fully merged into this document's own §4/§6/§10 during the earlier doc-consolidation pass, but the in-text references were never updated. A fresh agent following those links would hit dead ends repeatedly. Bulk-replaced with in- document section references; hand-fixed ~4 sentences that framed §6/ §10 as "sibling task, in progress" (stale - both are complete, this document's §6/§10 already are the settled contract, nothing to wait on). Added "Recommended default" to every item across the document's 5 consolidated "Requires backend decision" registers (§2.12 framework, §3.21 CRUD cross-cutting - 18 items, §4 §12 auth open items - 9 items, §6 error-model summary - 9 items, §10 maintenance-mode list - 5 items). Each default is derived from what the frontend already implies or standard REST/security convention - no invented APIs or business rules. ~6 items are explicitly flagged as real business/security decisions instead (order state-machine rules, Ed25519 cutover strategy, refresh-token reuse-detection posture) since those carry consequences no amount of frontend-code-reading can resolve. Added a "How to use this document" preamble up front: work in §9's dependency order not document order, apply recommended defaults and keep moving, only stop for the explicitly-flagged business/security items, don't invent beyond what's written or directly implied. Frontend side: confirmed nothing else is left. docs/TODO.md already has zero blockers; docs/KNOWN-ISSUES.md's one open item (unreachable Ed25519 error-code UI) is correctly left open and documented rather than faked closed - fixing it needs a real backend emitting real distinguishable error codes, which doesn't exist yet and can't be fabricated without inventing an API contract. No frontend code touched. No architecture changed. No APIs invented.
2026-07-26 16:10:33 +04:00
**Recommended default:** offset-paginated `GET` with a `since`/time-range
filter (matches §2.2's standard pagination contract) — no need for a
streaming protocol for an admin-facing log view.
17. **Analytics**: `conversionRate`/`retentionPercent` must stay `null` when
unknown; no traffic/visitor data source exists anywhere — real analytics is a
from-scratch backend build.
docs: finalize BACKEND.md as a self-contained implementation prompt Goal: this document alone, pasted into a fresh Claude session, should be enough for a backend dev to implement against without getting stuck or inventing conventions the frontend doesn't actually need. Fixed a real bug: ~65 cross-references throughout the document pointed to docs/AUTHENTICATION.md, docs/ERROR_CONTRACT.md, docs/MAINTENANCE_MODE.md - three sibling docs that were deleted and fully merged into this document's own §4/§6/§10 during the earlier doc-consolidation pass, but the in-text references were never updated. A fresh agent following those links would hit dead ends repeatedly. Bulk-replaced with in- document section references; hand-fixed ~4 sentences that framed §6/ §10 as "sibling task, in progress" (stale - both are complete, this document's §6/§10 already are the settled contract, nothing to wait on). Added "Recommended default" to every item across the document's 5 consolidated "Requires backend decision" registers (§2.12 framework, §3.21 CRUD cross-cutting - 18 items, §4 §12 auth open items - 9 items, §6 error-model summary - 9 items, §10 maintenance-mode list - 5 items). Each default is derived from what the frontend already implies or standard REST/security convention - no invented APIs or business rules. ~6 items are explicitly flagged as real business/security decisions instead (order state-machine rules, Ed25519 cutover strategy, refresh-token reuse-detection posture) since those carry consequences no amount of frontend-code-reading can resolve. Added a "How to use this document" preamble up front: work in §9's dependency order not document order, apply recommended defaults and keep moving, only stop for the explicitly-flagged business/security items, don't invent beyond what's written or directly implied. Frontend side: confirmed nothing else is left. docs/TODO.md already has zero blockers; docs/KNOWN-ISSUES.md's one open item (unreachable Ed25519 error-code UI) is correctly left open and documented rather than faked closed - fixing it needs a real backend emitting real distinguishable error codes, which doesn't exist yet and can't be fabricated without inventing an API contract. No frontend code touched. No architecture changed. No APIs invented.
2026-07-26 16:10:33 +04:00
**Recommended default:** ship order/product/revenue aggregates first (real
data, computable from orders/products already in scope); leave traffic-based
metrics `null` until a real analytics/traffic pipeline exists — never
fabricate a number, the frontend already renders `null` as "Unknown."
18. **Variants/options** shape reconciliation between storefront `variantOptions`
(grouped) and admin `variants` (flat priced rows); no dedicated variants
endpoint (created within the product body).
docs: finalize BACKEND.md as a self-contained implementation prompt Goal: this document alone, pasted into a fresh Claude session, should be enough for a backend dev to implement against without getting stuck or inventing conventions the frontend doesn't actually need. Fixed a real bug: ~65 cross-references throughout the document pointed to docs/AUTHENTICATION.md, docs/ERROR_CONTRACT.md, docs/MAINTENANCE_MODE.md - three sibling docs that were deleted and fully merged into this document's own §4/§6/§10 during the earlier doc-consolidation pass, but the in-text references were never updated. A fresh agent following those links would hit dead ends repeatedly. Bulk-replaced with in- document section references; hand-fixed ~4 sentences that framed §6/ §10 as "sibling task, in progress" (stale - both are complete, this document's §6/§10 already are the settled contract, nothing to wait on). Added "Recommended default" to every item across the document's 5 consolidated "Requires backend decision" registers (§2.12 framework, §3.21 CRUD cross-cutting - 18 items, §4 §12 auth open items - 9 items, §6 error-model summary - 9 items, §10 maintenance-mode list - 5 items). Each default is derived from what the frontend already implies or standard REST/security convention - no invented APIs or business rules. ~6 items are explicitly flagged as real business/security decisions instead (order state-machine rules, Ed25519 cutover strategy, refresh-token reuse-detection posture) since those carry consequences no amount of frontend-code-reading can resolve. Added a "How to use this document" preamble up front: work in §9's dependency order not document order, apply recommended defaults and keep moving, only stop for the explicitly-flagged business/security items, don't invent beyond what's written or directly implied. Frontend side: confirmed nothing else is left. docs/TODO.md already has zero blockers; docs/KNOWN-ISSUES.md's one open item (unreachable Ed25519 error-code UI) is correctly left open and documented rather than faked closed - fixing it needs a real backend emitting real distinguishable error codes, which doesn't exist yet and can't be fabricated without inventing an API contract. No frontend code touched. No architecture changed. No APIs invented.
2026-07-26 16:10:33 +04:00
**Recommended default:** keep variants embedded in the product body (no
separate variants endpoint) — matches how the admin editor already sends
them; the backend computes/derives the storefront's grouped
`variantOptions` shape from the same flat rows at read time.
---
_All paths repo-relative to `F:\dx\remote\marketplaces\`. Storefront/category
literals and the `AdminCategoriesApiGateway` paths are verified in source; every
other endpoint is PROPOSED per the conventions in §3.0._
---
## 4. Authentication
Standalone, backend-implementable authentication contract for the marketplace
platform (Angular frontend, branch `B2B`). Derived directly from source —
`src/app/core/auth/**`, `src/app/core/admin-auth/**`,
`src/app/services/{auth,telegram-session-api}.service.ts`,
`src/app/components/telegram-login/**`, `src/app/guards/language.guard.ts`,
`src/app/app.routes.ts`, `src/app/app.config.ts`,
`src/app/core/config/tenant-resolver.service.ts` — plus
`docs/context/BACKEND-AUDIT.md` (this-session audit) and the prior
`docs/AUTH.md`. Where the frontend does not already imply a behavior, this
document says **"Requires backend decision"** rather than inventing one.
Two authentication mechanisms coexist in the codebase today, at different
maturity levels:
| Mechanism | Used by | Status |
|---|---|---|
| Telegram QR / deep-link session auth | Storefront customers **and** admin/backoffice (same API) | **LIVE** — real endpoints, in production use |
| Ed25519 challenge/response admin auth | Admin/backoffice (intended replacement) | **Frontend fully wired, backend endpoints do not exist yet** (404s today) |
Both are documented in full below. Nothing here should be read as "the
platform has JWTs today" — it does not, except inside the not-yet-live
Ed25519 flow.
---
### Table of contents
1. [Mechanism A — Telegram QR / session login (LIVE)](#1-mechanism-a--telegram-qr--session-login-live)
2. [Mechanism B — Ed25519 challenge/response admin auth (NOT LIVE)](#2-mechanism-b--ed25519-challengeresponse-admin-auth-not-live)
3. [JWT structure](#3-jwt-structure)
4. [Refresh token](#4-refresh-token)
5. [Token expiration handling](#5-token-expiration-handling)
6. [Token rotation](#6-token-rotation)
7. [Logout](#7-logout)
8. [Session invalidation](#8-session-invalidation)
9. [Role hierarchy](#9-role-hierarchy)
10. [Tenant isolation](#10-tenant-isolation)
11. [Permission model / route guards](#11-permission-model--route-guards)
12. [Open items — "Requires backend decision"](#12-open-items--requires-backend-decision)
---
### 1. Mechanism A — Telegram QR / session login (LIVE)
Single source for **both** customer and admin login:
`TelegramSessionApiService` (`src/app/services/telegram-session-api.service.ts`).
There is no separate admin backend endpoint — the same three calls back the
customer `AuthService` (`src/app/services/auth.service.ts`) and the admin
`AdminAuthService` (`src/app/core/admin-auth/admin-auth.service.ts`). Only the
**storage** differs (cookie name, in-memory signal), so an admin QR scan
never authenticates the customer session or vice versa.
#### 1.1 Endpoints (base = `environment.authApiUrl`, e.g. `https://api.dexarmarket.ru:445`)
| Method | Path | Request | Response |
|---|---|---|---|
| POST | `/users/sessions` | body `{ webSessionID }` (client-generated GUID), header `WebSessionID: <same guid>` | `{ webSessionID, url }``url` is the Telegram bot deep link |
| GET | `/users/sessions/{id}` | — | Session object, heavily field-tolerant (see §1.3) |
| DELETE | `/users/sessions/{id}` | header `WebSessionID: <id>` | ignored/discarded |
#### 1.2 Frontend-driven flow
The frontend, not the backend, generates the session id. Sequence:
1. User opens login (storefront "Sign in" or admin `/admin-login` gate).
2. Frontend generates a random GUID client-side (`generateGuid()`,
`src/app/shared/util/guid.util.ts`) — this **is** the `webSessionID`, sent
to the backend, not received from it.
3. `POST {authApiUrl}/users/sessions` with `{ webSessionID }` body and
`WebSessionID` header set to the same value. Backend response's own id
field is preferred if present (see `extractSessionId` — checks
`webSessionID/WebSessionID/webSessionId/sessionID/SessionID/sessionId/id/ID`
in that order), else the frontend's generated GUID is used as fallback.
4. Frontend builds two login links from the returned id:
- Web: `https://t.me/{bot}?start={webSessionID}` (`getBotLoginUrl`)
- App deep link: `tg://resolve?domain={bot}&start={webSessionID}`
(`getBotAppLoginUrl`)
- `bot` = `environment.telegramBot` (`'myAMLKYCBOT'` in current env config;
code fallback `'DexarSupport_bot'` if the env key is absent).
5. Frontend renders both as a QR code (external image generator
`https://api.qrserver.com/v1/create-qr-code/...` — not a backend of this
platform, purely a QR bitmap renderer for the `url`) plus the app deep
link for mobile. This is orchestrated by `QrLoginEngine`
(`src/app/shared/qr-login/qr-login.engine.ts`) shared identically by both
customer and admin modes via `TelegramLoginComponent`
(`src/app/components/telegram-login/telegram-login.component.ts`,
`[mode]="'customer' | 'admin'"`).
6. User scans the QR (or taps the deep link on mobile) and completes the
Telegram bot interaction out-of-band. The **backend** is expected to mark
that `webSessionID` as active/logged-in once the Telegram bot confirms the
user, associating a Telegram user identity with it.
7. Frontend polls `GET /users/sessions/{id}` (`checkSessionOnce`, driven by
`QrLoginEngine`'s polling loop) until the session normalizes to `active:
true`, or the user cancels/times out.
8. On an active session, the frontend calls `activateSession()` internally
(sets in-memory signal, stores the id per §1.4, schedules a re-check —
see §5) and redirects: customer → wherever the login was triggered from;
admin → `/{lang}/backoffice/dashboard` (hardcoded in
`telegram-login.component.ts`).
#### 1.3 Session response normalization (backend field tolerance)
`TelegramSessionApiService.normalizeWebSession()` is deliberately
tolerant of multiple backend field-naming conventions (evidence the backend
contract was never fully pinned down). A backend implementation can emit any
of these; the frontend reads the **first key found** in this priority order:
- **Active/status**: `status`, `Status`, `active`, `Active`, `loggedIn`,
`LoggedIn`, `isLoggedIn`, `IsLoggedIn`, `authenticated`, `Authenticated`.
Value is considered "active" if boolean `true`/`1`, or (case-insensitive)
one of `true, 1, active, authenticated, confirmed, success, logged_in`.
- **User object**: nested under `user`/`User`/`telegramUser`/`TelegramUser`,
else the top-level response object itself is used as the user record.
- **Username**: `username`/`Username` (user object first, then top-level).
- **First/last name**: `firstName`/`first_name`/`FirstName`/`First_name`,
`lastName`/`last_name`/`LastName`/`Last_name` — joined with a space if both
present.
- **Display name**: explicit `displayName`/`DisplayName`/`name`/`Name` (user
object or top-level) wins; else falls back to `username`; else falls back
to the joined full name; else literal `'Telegram User'`.
- **Telegram user id**: `userId`/`telegramUserId`/`telegramUserID`/
`TelegramUserID`/`id`/`ID` (user object), else `userId`/`telegramUserId`/
`telegramUserID`/`TelegramUserID`/`userID`/`UserID`/`UserId` (top-level).
- **Session id**: same priority list as `extractSessionId` above.
- **Expiry**: `expiresAt`/`ExpiresAt`/`expires`/`Expires` (ISO 8601 string);
if absent, the frontend fabricates `now + 3600s` client-side — **the
backend should always send a real `expiresAt`/`expires`** so the frontend's
refresh-scheduling (§5) reflects the true session lifetime rather than a
guessed one.
Normalized shape consumed by the frontend (`AuthSession`,
`src/app/models/auth.model.ts`):
```ts
interface AuthSession {
sessionId: string;
userId: number | null;
username: string | null;
displayName: string;
active: boolean;
expires: string; // ISO 8601
}
```
#### 1.4 Storage (customer vs admin — kept fully separate)
| | Customer (`AuthService`) | Admin (`AdminAuthService`) |
|---|---|---|
| Cookie name | `webSessionID` | `adminSessionID` |
| Cookie attrs | `Max-Age=3600; Path=/; SameSite=Lax` (+`Secure` over HTTPS) | `Max-Age=3600; Path=/; SameSite=Strict` (+`Secure` over HTTPS) |
| In-memory state | `sessionSignal`, `statusSignal` (`unknown\|checking\|authenticated\|expired\|unauthenticated`) | Same shape, separate signals |
| Extra storage | — | Reserved JWT-pair slots `localStorage['adminToken']` / `localStorage['adminRefreshToken']`**unused today**, see §12 |
Both send a `WebSessionID` header on every marketplace API request via
`apiHeadersInterceptor` (see `docs/context/BACKEND-AUDIT.md` §3) — this is
the anonymous-or-authenticated session identity the backend correlates
requests against; there is no `Authorization: Bearer` header in this
mechanism.
#### 1.5 Session re-check / soft refresh (not a token refresh)
Both `AuthService` and `AdminAuthService` self-schedule a re-check of
`GET /users/sessions/{id}` 60 seconds before `expires`, minimum 30s out
(`scheduleSessionRefresh`). This is **not** a refresh-token exchange — it
just re-polls the same session-status endpoint and re-activates if still
active, or clears local state if not. There is no rotation of the
`webSessionID` itself in this mechanism.
#### 1.6 Admin dev bypass (non-production only)
`AdminAuthService.devBypassLogin()` fabricates a local session
(`sessionId: 'dev-bypass-{timestamp}'`, `active: true`, 1-hour expiry) and
activates it directly, skipping the QR flow entirely. Guarded by
`environment.production` at runtime (not just build-time) — the checked
condition is inside the function body, so it is dead code in a production
build.
#### 1.7 Sequence diagram — storefront/admin Telegram-QR login
```mermaid
sequenceDiagram
participant User as User (browser)
participant FE as Frontend (AuthService / AdminAuthService)
participant BE as Backend (authApiUrl)
participant TG as Telegram bot
User->>FE: Open login (customer checkout, or /admin-login gate)
FE->>FE: generate webSessionID (client GUID)
FE->>BE: POST /users/sessions { webSessionID } (header WebSessionID)
BE-->>FE: 200 { webSessionID, ... }
FE->>FE: build QR + tg:// deep link from webSessionID
FE-->>User: render QR code / "Open in Telegram" button
User->>TG: scan QR / tap deep link, confirm in bot
TG->>BE: (out of band) associate webSessionID with Telegram user
loop poll every N seconds
FE->>BE: GET /users/sessions/{webSessionID}
BE-->>FE: session (active:false while pending)
end
BE-->>FE: session (active:true, user fields, expires)
FE->>FE: activateSession(): store cookie, set signals,<br/>schedule re-check at expires-60s
alt mode = admin
FE-->>User: redirect to /{lang}/backoffice/dashboard
else mode = customer
FE-->>User: close dialog, resume prior action (e.g. checkout)
end
```
---
### 2. Mechanism B — Ed25519 challenge/response admin auth (NOT LIVE)
**Status: frontend fully implemented and wired to real `HttpClient` calls;
the backend does not implement these endpoints yet — calls 404/error today.**
No route currently requires this flow (`ed25519AuthGuard` is not referenced
by any route in `app.routes.ts`; the live admin gate is still
`adminAuthGuard` / Telegram QR, §1). This is the target contract for closing
the security gap in §1: today the Telegram session API has no concept of
"admin," so the backend cannot distinguish an admin login attempt from a
customer one at the moment of login. Ed25519 closes that by requiring proof
of possession of a specific, pre-registered private key before any session
is issued.
#### 2.1 Key generation (device-local, once per device)
`Ed25519KeypairService` (`src/app/core/auth/services/ed25519-keypair.service.ts`):
- `getOrCreateKeyPair()`: generates a **non-extractable** Ed25519 keypair via
`crypto.subtle.generateKey({ name: 'Ed25519' }, false, ['sign', 'verify'])`
(real WebCrypto Ed25519 — RFC 8032, not a placeholder), persists the raw
`CryptoKey` handles in IndexedDB (`admin-auth-ed25519` DB, object store
`keypair`, single record `id: 'device-keypair'`).
- The private key is never exported, serialized, or transmitted — by
construction (`extractable: false`), not by convention or policy.
- `sign(message)`: signs a UTF-8-encoded string with `crypto.subtle.sign
('Ed25519', privateKey, ...)`, returns a base64-encoded signature.
- `clear()`: deletes the IndexedDB record ("forget this device"). A new
keypair generated after this requires re-registration with the backend
(§2.2) before it can complete a login.
- **Public key registration is explicitly out of scope for the frontend.**
An Owner/Administrator must associate a new device's `publicKeyBase64`
with an admin account through some out-of-band mechanism (backend admin
tool, one-time enrollment link, etc.) — not prescribed here (see §12).
#### 2.2 Login flow, step by step
Orchestrated by `AuthService.login()` (`src/app/core/auth/services/auth.service.ts`,
distinct from the customer/admin `AuthService` in §1 despite the identical
class name — different module, `core/auth/` vs `services/`):
1. `GET {authApiUrl}/api/admin/auth/challenge` → `AuthChallenge { nonce,
issuedAt, expiresAt }` (all ISO 8601 except `nonce`, an opaque string).
2. `Ed25519KeypairService.getOrCreateKeyPair()` (generates on first use).
3. `Ed25519KeypairService.sign(nonce)` — signs the **raw nonce string
exactly as received**, no additional framing/prefix/hashing applied
client-side.
4. `POST {authApiUrl}/api/admin/auth/verify` with body
`VerifySignatureRequest { publicKey, signature, nonce }` (`publicKey` =
base64 raw Ed25519 public key, `signature` = base64 signature over the
nonce, `nonce` = the same value echoed back).
5. Backend must: re-derive the exact signed message from the nonce it
issued, verify the signature against its own `publicKey → admin account`
mapping, confirm the nonce hasn't expired or been used before, and only
then issue tokens.
6. On success: `200 AuthTokenPair { token, refreshToken }`.
`SessionService.activate(tokens)` decodes the JWT (§3), stores both
tokens (§4), and schedules the next refresh (§5).
7. On failure: `401`/`403``AuthService` maps it through
`authErrorCodeFromStatus()` to `invalid-signature` (or a more specific
code — see §2.5) and the UI routes to
`/admin-login/error/invalid-signature`.
#### 2.3 API contracts (all under `{environment.authApiUrl}/api/admin/auth`)
| Method | Path | Request body | Response | Notes |
|---|---|---|---|---|
| GET | `/challenge` | — | `200 AuthChallenge` | `{ nonce, issuedAt, expiresAt }` |
| POST | `/verify` | `VerifySignatureRequest { publicKey, signature, nonce }` | `200 AuthTokenPair` \| `401` \| `403` | Issues `{ token, refreshToken }` |
| POST | `/refresh` | `RefreshTokenRequest { refreshToken }` | `200 AuthTokenPair` \| `401` | Rotation expected — see §6 |
| POST | `/logout` | `{ refreshToken }` | `204` (frontend clears local state regardless of response code/body) | Should revoke server-side |
Types: `src/app/core/auth/models/auth-api.model.ts`. HTTP client:
`src/app/core/auth/services/auth-api.service.ts` (`AuthApiService`) — thin
wrapper, no retries, no fabricated mock responses.
#### 2.4 Sequence diagram — admin login with Ed25519 signing
```mermaid
sequenceDiagram
participant Admin as Admin (browser)
participant FE as Frontend (AuthService, core/auth)
participant Key as Ed25519KeypairService (WebCrypto + IndexedDB)
participant BE as Backend
Admin->>FE: Click "Sign in"
FE->>BE: GET /api/admin/auth/challenge
BE-->>FE: 200 { nonce, issuedAt, expiresAt }
FE->>Key: getOrCreateKeyPair() (generate on first use, non-extractable)
Key-->>FE: { publicKeyBase64 }
FE->>Key: sign(nonce)
Key-->>FE: signature (base64)
FE->>BE: POST /api/admin/auth/verify { publicKey, signature, nonce }
alt signature valid & publicKey is a provisioned admin key & nonce fresh/unused
BE-->>FE: 200 { token, refreshToken }
FE->>FE: SessionService.activate(tokens):<br/>decode JWT claims, persist, schedule refresh
FE-->>Admin: redirect to /backoffice
else invalid signature / unknown key / expired or reused nonce
BE-->>FE: 401 / 403
FE-->>Admin: redirect to /admin-login/error/invalid-signature
end
```
#### 2.5 Error screens
Single component `AuthErrorPageComponent` at route `/admin-login/error/:code`
renders all five, keyed by route param. `authErrorCodeFromStatus()`
(`src/app/core/auth/models/auth-error.model.ts`) maps HTTP status → code:
`401→unauthorized`, `403→forbidden`, `0→backend-unavailable`,
`5xx→backend-unavailable`, else `unauthorized`.
| Code | Trigger | User action offered |
|---|---|---|
| `session-expired` | Refresh token rejected/expired | Sign in again |
| `invalid-signature` | `verify` returns 401/403 during login, or any client-side failure in the challenge→sign→verify chain that isn't a clearer HTTP-derived code | Try again |
| `unauthorized` | Route guard sees no active session | Sign in |
| `forbidden` | `permissionGuard` denies (authenticated but insufficient role) | Back to dashboard |
| `backend-unavailable` | Network error / 5xx / status 0 | Retry |
#### 2.6 Interceptor status — NOT registered
`src/app/core/auth/interceptors/auth.interceptor.ts` exists (adds
`Authorization: Bearer` + reactive 401-refresh-and-retry, see §5) but **is
not included** in `app.config.ts`'s `withInterceptors([...])` list today.
Confirmed in `app.config.ts`:
```
withInterceptors([mockDataInterceptor, apiBaseUrlInterceptor,
apiHeadersInterceptor, adminAuthHeadersInterceptor, cacheInterceptor])
```
`authInterceptor` is absent. Until it is registered, no request in the app
automatically attaches the Ed25519-flow JWT as a bearer token — this
confirms the mechanism is fully dormant, not partially live.
#### 2.7 Module map
```
src/app/core/auth/
├── auth.routes.ts # /admin-login, /admin-login/error/:code
├── models/
│ ├── auth-api.model.ts # AuthChallenge, VerifySignatureRequest, AuthTokenPair, JwtClaims
│ ├── auth-error.model.ts # AuthErrorCode, authErrorCodeFromStatus()
│ └── permission.model.ts # AdminRole, Permission, ROLE_PERMISSIONS
├── services/
│ ├── ed25519-keypair.service.ts # WebCrypto keygen/sign, IndexedDB persistence
│ ├── auth-api.service.ts # HttpClient calls to the 4 endpoints in §2.3
│ ├── jwt.service.ts # decode-only JWT parsing
│ ├── session.service.ts # token/claims state, persistence, refresh scheduling
│ ├── permission.service.ts # role -> permission set
│ ├── auth.service.ts # orchestrates challenge -> sign -> verify -> refresh -> logout
│ └── auth-facade.service.ts # public surface for components
├── interceptors/
│ └── auth.interceptor.ts # Authorization: Bearer + 401 refresh-and-retry (NOT registered, §2.6)
├── guards/
│ ├── ed25519-auth.guard.ts # requires SessionService.isAuthenticated() (not referenced by any route)
│ └── permission.guard.ts # permissionGuard(permission) factory
└── pages/
├── admin-login-page.component.* # sign-in UI
└── auth-error-page.component.* # parameterized error screen (§2.5)
```
`AuthFacade` (`src/app/core/auth/services/auth-facade.service.ts`) is the
only thing components/pages should depend on; `AuthService`/
`SessionService`/`PermissionService` are internal collaborators.
---
### 3. JWT structure
Only defined for Mechanism B (Ed25519 flow) — Mechanism A (§1) issues no JWT,
only an opaque session id. Expected claims
(`src/app/core/auth/models/auth-api.model.ts::JwtClaims`):
```ts
interface JwtClaims {
sub: string; // admin account id
role: AdminRole; // 'Owner' | 'Administrator' | 'Editor' | 'Support' | 'ReadOnly'
iat: number; // seconds since epoch (standard `iat`)
exp: number; // seconds since epoch (standard `exp`)
publicKey: string; // the Ed25519 public key this token was issued for
}
```
`JwtService.decode()` (`src/app/core/auth/services/jwt.service.ts`) does
**decode-only** parsing (base64url payload → JSON), and validates only the
minimal shape: `sub` is a string, `role` is a string, `exp` is a number — if
any of these three checks fail, decoding returns `null` and the caller
(`SessionService`) discards the session as malformed.
The frontend never verifies the JWT signature — it has no trusted key to
check it against; that is exclusively the backend's job on every
subsequent admin request. A decoded-but-unverified claim is UX (role-gated
menus, expiry countdowns) — never proof of authorization to any client-side
check.
---
### 4. Refresh token
Defined only for Mechanism B. `AuthTokenPair { token, refreshToken }` is
returned by both `/verify` and `/refresh`. Storage
(`SessionService`, `src/app/core/auth/services/session.service.ts`):
- `localStorage['ed25519AdminToken']` — access token (JWT)
- `localStorage['ed25519AdminRefreshToken']` — refresh token (opaque to the
frontend; never decoded, only round-tripped)
Both are written together in `activate()` and cleared together in `clear()`.
There is no separate expiry tracked for the refresh token client-side —
the frontend only reacts to a `401` from `/refresh` (see §5/§6).
Separately, `AdminAuthService` (Mechanism A, Telegram) reserves
`localStorage['adminToken']` / `localStorage['adminRefreshToken']` with
`getAdminToken()`/`setAdminTokens()`/`clearAdminTokens()` methods —
**written by no code path today** ("reserved for once the backend issues
admin access/refresh tokens... unused until then," per the source comment).
These are a distinct, currently-dead pair of storage keys from the Ed25519
ones above; do not conflate them.
---
### 5. Token expiration handling
#### 5.1 Mechanism A (Telegram session) — expiry via re-poll
See §1.5. `expires` from the session payload drives a `setTimeout` at
`max(expiresMs - now - 60_000, 30_000)` that re-calls `GET /users/sessions/
{id}`; if the backend now reports inactive, local state is cleared to
`unauthenticated`. There is no interceptor-level reactive handling for this
mechanism — a 401/expired session surfaces only through the next explicit
`checkSessionOnce()` poll or session re-check timer, not a per-request
retry.
#### 5.2 Mechanism B (Ed25519/JWT) — proactive + reactive
`SessionService.scheduleRefresh(claims)`: computes
`refreshInMs = max(claims.exp*1000 - now - 60_000, 5_000)` and sets a timer.
When it fires, `AuthService.refresh()` runs automatically
(`session.onRefreshDue(callback)` wiring, set up once in `AuthService`'s
constructor to avoid a circular DI dependency between the two services).
`SessionService.restore()` (intended to run once at app bootstrap, from an
`APP_INITIALIZER` calling `AuthFacade.restoreSession()` — **not yet wired
into the bootstrap process today**, see §12): reads persisted tokens,
decodes claims, and either resumes with a scheduled refresh or marks
`expired` immediately without any network call, so a stale session is
caught before any component/guard runs.
`authInterceptor` (present in source, not registered — §2.6) is documented
as: catch a 401 on any admin-gated request → attempt one `refresh()`
retry the original request once on success → route to `session-expired` on
failure. Does not retry more than once; a second 401 after an
apparently-successful refresh is treated as a server-side problem, not a
transient race.
#### 5.3 Sequence diagram — token expiration / refresh (Ed25519 flow)
```mermaid
sequenceDiagram
participant FE as Frontend (SessionService)
participant IC as authInterceptor (not yet registered, §2.6)
participant BE as Backend
Note over FE: Timer fires ~60s before JWT exp
FE->>BE: POST /api/admin/auth/refresh { refreshToken }
alt refresh token still valid
BE-->>FE: 200 { token, refreshToken }
FE->>FE: activate(tokens) - reschedules next refresh
else refresh token expired/revoked
BE-->>FE: 401
FE->>FE: SessionService.markExpired()
FE-->>FE: route to /admin-login/error/session-expired
end
Note over IC: Reactive path - any 401 on an admin request<br/>(inactive until authInterceptor is registered)
IC->>BE: Admin API request (expired token)
BE-->>IC: 401
IC->>BE: POST /api/admin/auth/refresh (single retry)
alt refresh succeeds
BE-->>IC: 200 tokens
IC->>BE: retry original request with new token
else refresh fails
IC-->>FE: propagate error, route to session-expired
end
```
---
### 6. Token rotation
**Mechanism A**: no token to rotate — the `webSessionID` itself is stable
for the life of the session; expiry is handled by re-polling status (§5.1),
not by issuing a new id.
**Mechanism B**: rotation is *expected* by the frontend but not verifiable
until the backend exists. Per the source comment in `AuthApiService` and the
security notes in the prior `docs/AUTH.md`:
- Every `POST /refresh` response is expected to include a **new**
`refreshToken`; the backend should invalidate the one just used
(single-use refresh tokens).
- The frontend always stores whatever pair it receives from `/verify` or
`/refresh` and never reuses an old refresh token after a successful
rotation — there is no client-side retry logic that would resend a stale
refresh token.
- **Requires backend decision**: refresh-token reuse detection / revocation
cascade (e.g. if a rotated-out refresh token is presented again, should the
backend revoke the entire token family as a compromise signal?). Nothing
in the frontend implies or depends on this — it is a pure backend policy
choice.
---
### 7. Logout
**Mechanism A** (`AdminAuthService.logout()` / `AuthService.logout()` in
`src/app/services/auth.service.ts`): `DELETE /users/sessions/{id}` with
`WebSessionID` header, then unconditionally clears local state (cookie,
signals, timers) regardless of the HTTP result.
**Mechanism B** (`AuthService.logout()` in `src/app/core/auth/services/`):
clears `SessionService` state **immediately and unconditionally** (before
the network call resolves), then best-effort calls
`POST /api/admin/auth/logout { refreshToken }` if a refresh token was
present; any error from that call is swallowed (`catchError(() =>
throwError(() => null))`). If no refresh token exists locally, no network
call is made at all. **Backend implication**: the frontend cannot be relied
upon to reliably deliver the logout call (network failure, tab closed
mid-request, etc.) — server-side session/token expiry must not depend on a
client-issued logout ever arriving. `AuthFacade.logout()` additionally
always navigates to `/admin-login` (default) via `finalize()`, regardless of
API outcome.
#### 7.1 Sequence diagram — logout (both mechanisms)
```mermaid
sequenceDiagram
participant User
participant FE as Frontend
participant BE as Backend
User->>FE: Click "Log out"
FE->>FE: Clear local session state immediately<br/>(cookie / tokens / signals / timers)
alt Mechanism A (Telegram session)
FE->>BE: DELETE /users/sessions/{id} (header WebSessionID)
BE-->>FE: any response (ignored)
else Mechanism B (Ed25519/JWT) - only if a refresh token existed
FE->>BE: POST /api/admin/auth/logout { refreshToken }
BE-->>FE: 204 (or error, swallowed)
end
FE-->>User: redirect to login page
```
---
### 8. Session invalidation
Client-side triggers that clear local auth state, both mechanisms:
- Explicit logout (§7).
- Session status re-check (Mechanism A) returning `active: false` (§1.5).
- JWT decode failure on restore (Mechanism B) — a malformed/unparsable
stored token is treated as no session at all (`SessionService.restore()`
calls `clear()`).
- Refresh failure (Mechanism B) — any error from `/refresh` calls
`SessionService.markExpired()`.
- `AdminAuthService.clearAuthState()` also clears the reserved
`adminToken`/`adminRefreshToken` keys (§4) even though nothing currently
writes them, for forward-compatibility once Mechanism A gains a token
pair.
**Requires backend decision**: server-side session/token revocation
propagation — e.g., can an Owner revoke another admin's active session
remotely (relevant given `AdminUsersGateway.revokeSession` already exists as
a **mock-only** admin-users gateway method per
`docs/context/BACKEND-AUDIT.md` §14)? If so, the frontend has no push
mechanism (no websocket, no polling of "is my token still valid" beyond the
scheduled refresh) to learn about a remote revocation before its next
refresh/request attempt — a session could remain "authenticated" client-side
for up to the refresh interval after a backend-side revocation. If real-time
revocation is required, that is new frontend work, not something already
implied by existing code.
---
### 9. Role hierarchy
**Discrepancy flagged by the backend audit — `AdminRole` is defined twice
with different meanings. Reconciliation needed before backend
implementation:**
1. `src/app/core/auth/models/permission.model.ts` — a **string union** used
by the Ed25519/JWT flow's `role` claim and `PermissionService`:
```ts
type AdminRole = 'Owner' | 'Administrator' | 'Editor' | 'Support' | 'ReadOnly';
```
Ordered highest-to-lowest privilege by convention (not enforced in code —
`PermissionService` does not rely on ordering, only exact role → permission
set lookup).
2. `src/app/features/admin/users/models/admin-user.model.ts` — an
**interface** describing an admin-users-management row (`{ id, name, ...
}`), unrelated in shape to #1 and used only by the mock admin-users
gateway/facade (`AdminUsersGateway`, MOCK-ONLY, no backend seam per the
audit).
These two `AdminRole` symbols do not currently reference each other and are
imported from different modules by different features. A backend
implementer should treat #1 (the permission-model union) as the JWT/role
claim contract for auth purposes, and flag #2 for a naming rename (e.g.
`AdminUserRoleRecord`) rather than assuming they describe the same concept.
This document does not resolve the collision — it is called out so a human
reconciles it before building the backend role table.
#### 9.1 Permission-to-role mapping (from `ROLE_PERMISSIONS`)
| Role | Permissions |
|---|---|
| `Owner` | `backoffice.read`, `backoffice.write`, `builder.read`, `builder.write`, `users.manage`, `settings.manage` |
| `Administrator` | `backoffice.read`, `backoffice.write`, `builder.read`, `builder.write`, `users.manage` |
| `Editor` | `backoffice.read`, `backoffice.write`, `builder.read`, `builder.write` |
| `Support` | `backoffice.read` |
| `ReadOnly` | `backoffice.read`, `builder.read` |
This is deliberately coarse and mirrors the bootstrap-level
`PermissionsConfig` shape (`src/app/shared/models/config/permissions.model.ts`).
Finer-grained, per-domain permissions (e.g. "can edit prices but not delete
products") do not exist anywhere client-side and stay server-side —
**Requires backend decision** if finer granularity is ever needed.
---
### 10. Tenant isolation
`TenantResolverService` (`src/app/core/config/tenant-resolver.service.ts`)
resolves tenant by **subdomain**, not by header or path prefix:
```ts
getTenantKey(): string {
if (isLocalhost()) return environment.fallbackTenantKey ?? 'default';
const segments = hostname.split('.').filter(Boolean);
if (segments.length === 0) return environment.fallbackTenantKey ?? 'default';
if (segments[0] === 'www' && segments.length > 1) return segments[1];
return segments[0];
}
```
- `isLocalhost()` matches `localhost`, `127.0.0.1`, `::1`.
- On a real host, the tenant key is the **first DNS label**, skipping a
leading `www`. E.g. `dexarmarket.api.dexarmarket.ru` → tenant key
`dexarmarket`; `www.acme.com``acme`.
- This tenant key feeds `ApiConfigService.getBaseUrl()`
(`src/app/core/config/api-config.service.ts`, documented in
`docs/context/BACKEND-AUDIT.md` §2) to pick the marketplace API base URL:
localhost → `environment.localhostApiUrl` (`/api`); else
`environment.tenantApiBaseUrls[tenantKey]`; else
`environment.tenantApiTemplate` with `{tenant}` substituted; else
(gated by `allowBootstrapApiOverride`, off by default) a value read out of
the already-loaded bootstrap document
(`bootstrap.apiEndpoints.website.baseUrl` /
`bootstrap.tenant.apiBaseUrl`).
- **No `X-Tenant` header or `/tenant/{id}/...` path prefix is sent by the
frontend anywhere** — tenant isolation for the marketplace API is achieved
purely by **which base URL/subdomain the request is sent to**, not by a
request attribute the backend reads per-call. Auth (both mechanisms) does
**not** carry any tenant identifier in its request bodies or headers
either — `POST /users/sessions`, `/api/admin/auth/challenge`, etc. are all
called against `environment.authApiUrl`, a single fixed origin, with no
per-tenant variation in the auth-flow code today.
- **Requires backend decision**: if auth (session creation, Ed25519
challenge/verify) must be tenant-scoped (e.g. an admin's Ed25519 public key
should only authorize them for one tenant's backoffice), the frontend
currently has no mechanism to communicate which tenant a login attempt is
for beyond whatever the backend can infer from the request's origin/
Referer header — nothing in the auth payloads carries a tenant id
explicitly. This would be new frontend work if required.
---
### 11. Permission model / route guards
#### 11.1 `adminAuthGuard` (live, Mechanism A) — `src/app/core/admin-auth/admin-auth.guard.ts`
```ts
export const adminAuthGuard: CanActivateFn = () => {
const adminAuth = inject(AdminAuthService);
if (adminAuth.isAuthenticated()) return true;
adminAuth.requestLogin();
return false;
};
```
Checks only `AdminAuthService.isAuthenticated()` (Telegram session status
`=== 'authenticated'`) — no role/permission check at all. Applied to
`/edit`, `/edit/:section`, and `/backoffice` (and its children) in
`app.routes.ts`. This guard **cannot** distinguish admin roles from each
other — it is purely "is there an active admin Telegram session," which is
the exact gap Mechanism B is meant to close.
#### 11.2 `ed25519AuthGuard` (dormant, Mechanism B) — `src/app/core/auth/guards/ed25519-auth.guard.ts`
Requires `SessionService.isAuthenticated()` (JWT status `=== 'authenticated'`).
Not referenced by any route in `app.routes.ts` today — confirmed by source
search. Exists purely as the cutover target (see §2's "not live" status).
#### 11.3 `permissionGuard(permission)` — `src/app/core/auth/guards/permission.guard.ts`
Factory guard that checks `PermissionService.has(permission)` against the
Mechanism B role → permission table (§9.1). Also unused by any live route
until Mechanism B is cut over, but ready to gate specific admin sub-routes
by permission once it is (e.g. `permissionGuard('users.manage')` on a users
page).
#### 11.4 `languageGuard` — `src/app/guards/language.guard.ts`
Not an authentication guard, but gates every localized route (`:lang`
segment wraps the entire route tree in `app.routes.ts`). Behavior:
- If `:lang` param is a known, enabled language: preload its translation
pack (`TranslateService.preloadLanguage`), set it as current
(`LanguageService.setLanguage`), allow navigation.
- If known but **disabled**: redirect to the current default language,
preserving the rest of the path.
- If unrecognized entirely: treat the URL as a legacy no-lang-prefix URL and
redirect to `/{defaultLang}{originalUrl}`, preserving query string/fragment
via `router.parseUrl` (not a hand-built `UrlTree`, to avoid double-encoding
the query string into the path segment).
#### 11.5 `canDeactivate` guards (dirty-state guards, not auth)
Also not authentication, but listed since the task asked for "what guards
check": `projectEditorDirtyGuard`, `adminProductDirtyGuard`,
`adminCategoryDirtyGuard` — all gate navigation *away* from an in-progress
editor (builder section, product editor, category editor) to warn about
unsaved changes. They read editor dirty-state signals, not auth state, and
are unrelated to session/token validity.
#### 11.6 What the frontend actually gates, summarized
| Concern | Mechanism | Guard/service |
|---|---|---|
| "Is there an active admin session at all" | Telegram (A) | `adminAuthGuard``AdminAuthService.isAuthenticated()` |
| "Is there an active admin JWT session" | Ed25519 (B), not live | `ed25519AuthGuard``SessionService.isAuthenticated()` |
| "Does this role have permission X" | Ed25519 (B), not live | `permissionGuard(permission)``PermissionService.has()` |
| "Is `:lang` valid/enabled" | n/a | `languageGuard` |
| "Unsaved editor changes" | n/a | `*DirtyGuard` (project editor / product / category) |
**Every one of these is a client-side UX gate only.** None of them are a
substitute for server-side authorization — the backend must independently
verify role/permission on every admin mutation regardless of what a route
guard decided, per the security note already present in the prior
`docs/AUTH.md` and repeated here: a passing client-side check is not proof
of anything to the backend.
---
### 12. Open items — "Requires backend decision"
Consolidated list of everything this document could not derive from
docs: finalize BACKEND.md as a self-contained implementation prompt Goal: this document alone, pasted into a fresh Claude session, should be enough for a backend dev to implement against without getting stuck or inventing conventions the frontend doesn't actually need. Fixed a real bug: ~65 cross-references throughout the document pointed to docs/AUTHENTICATION.md, docs/ERROR_CONTRACT.md, docs/MAINTENANCE_MODE.md - three sibling docs that were deleted and fully merged into this document's own §4/§6/§10 during the earlier doc-consolidation pass, but the in-text references were never updated. A fresh agent following those links would hit dead ends repeatedly. Bulk-replaced with in- document section references; hand-fixed ~4 sentences that framed §6/ §10 as "sibling task, in progress" (stale - both are complete, this document's §6/§10 already are the settled contract, nothing to wait on). Added "Recommended default" to every item across the document's 5 consolidated "Requires backend decision" registers (§2.12 framework, §3.21 CRUD cross-cutting - 18 items, §4 §12 auth open items - 9 items, §6 error-model summary - 9 items, §10 maintenance-mode list - 5 items). Each default is derived from what the frontend already implies or standard REST/security convention - no invented APIs or business rules. ~6 items are explicitly flagged as real business/security decisions instead (order state-machine rules, Ed25519 cutover strategy, refresh-token reuse-detection posture) since those carry consequences no amount of frontend-code-reading can resolve. Added a "How to use this document" preamble up front: work in §9's dependency order not document order, apply recommended defaults and keep moving, only stop for the explicitly-flagged business/security items, don't invent beyond what's written or directly implied. Frontend side: confirmed nothing else is left. docs/TODO.md already has zero blockers; docs/KNOWN-ISSUES.md's one open item (unreachable Ed25519 error-code UI) is correctly left open and documented rather than faked closed - fixing it needs a real backend emitting real distinguishable error codes, which doesn't exist yet and can't be fabricated without inventing an API contract. No frontend code touched. No architecture changed. No APIs invented.
2026-07-26 16:10:33 +04:00
existing frontend code and therefore does not prescribe. Each has a
**Recommended default**; apply it unless the item is flagged as a real
business/security decision.
- **Public-key enrollment mechanism** (§2.1) — how an admin's Ed25519
`publicKeyBase64` gets associated with an account/role server-side (admin
tool? one-time enrollment link? manual DB entry?). Zero frontend code
exists for this by design.
docs: finalize BACKEND.md as a self-contained implementation prompt Goal: this document alone, pasted into a fresh Claude session, should be enough for a backend dev to implement against without getting stuck or inventing conventions the frontend doesn't actually need. Fixed a real bug: ~65 cross-references throughout the document pointed to docs/AUTHENTICATION.md, docs/ERROR_CONTRACT.md, docs/MAINTENANCE_MODE.md - three sibling docs that were deleted and fully merged into this document's own §4/§6/§10 during the earlier doc-consolidation pass, but the in-text references were never updated. A fresh agent following those links would hit dead ends repeatedly. Bulk-replaced with in- document section references; hand-fixed ~4 sentences that framed §6/ §10 as "sibling task, in progress" (stale - both are complete, this document's §6/§10 already are the settled contract, nothing to wait on). Added "Recommended default" to every item across the document's 5 consolidated "Requires backend decision" registers (§2.12 framework, §3.21 CRUD cross-cutting - 18 items, §4 §12 auth open items - 9 items, §6 error-model summary - 9 items, §10 maintenance-mode list - 5 items). Each default is derived from what the frontend already implies or standard REST/security convention - no invented APIs or business rules. ~6 items are explicitly flagged as real business/security decisions instead (order state-machine rules, Ed25519 cutover strategy, refresh-token reuse-detection posture) since those carry consequences no amount of frontend-code-reading can resolve. Added a "How to use this document" preamble up front: work in §9's dependency order not document order, apply recommended defaults and keep moving, only stop for the explicitly-flagged business/security items, don't invent beyond what's written or directly implied. Frontend side: confirmed nothing else is left. docs/TODO.md already has zero blockers; docs/KNOWN-ISSUES.md's one open item (unreachable Ed25519 error-code UI) is correctly left open and documented rather than faked closed - fixing it needs a real backend emitting real distinguishable error codes, which doesn't exist yet and can't be fabricated without inventing an API contract. No frontend code touched. No architecture changed. No APIs invented.
2026-07-26 16:10:33 +04:00
**Recommended default:** an authenticated admin-management endpoint
(`POST /backoffice/users/{id}/public-key`), callable only by an
already-authenticated Owner/Admin — treat it as a users-management
action (§3.5), not a public/unauthenticated enrollment flow.
- **Refresh-token reuse/compromise detection** (§6) — whether presenting an
already-rotated-out refresh token should revoke the whole token family.
Not implied by any frontend behavior.
docs: finalize BACKEND.md as a self-contained implementation prompt Goal: this document alone, pasted into a fresh Claude session, should be enough for a backend dev to implement against without getting stuck or inventing conventions the frontend doesn't actually need. Fixed a real bug: ~65 cross-references throughout the document pointed to docs/AUTHENTICATION.md, docs/ERROR_CONTRACT.md, docs/MAINTENANCE_MODE.md - three sibling docs that were deleted and fully merged into this document's own §4/§6/§10 during the earlier doc-consolidation pass, but the in-text references were never updated. A fresh agent following those links would hit dead ends repeatedly. Bulk-replaced with in- document section references; hand-fixed ~4 sentences that framed §6/ §10 as "sibling task, in progress" (stale - both are complete, this document's §6/§10 already are the settled contract, nothing to wait on). Added "Recommended default" to every item across the document's 5 consolidated "Requires backend decision" registers (§2.12 framework, §3.21 CRUD cross-cutting - 18 items, §4 §12 auth open items - 9 items, §6 error-model summary - 9 items, §10 maintenance-mode list - 5 items). Each default is derived from what the frontend already implies or standard REST/security convention - no invented APIs or business rules. ~6 items are explicitly flagged as real business/security decisions instead (order state-machine rules, Ed25519 cutover strategy, refresh-token reuse-detection posture) since those carry consequences no amount of frontend-code-reading can resolve. Added a "How to use this document" preamble up front: work in §9's dependency order not document order, apply recommended defaults and keep moving, only stop for the explicitly-flagged business/security items, don't invent beyond what's written or directly implied. Frontend side: confirmed nothing else is left. docs/TODO.md already has zero blockers; docs/KNOWN-ISSUES.md's one open item (unreachable Ed25519 error-code UI) is correctly left open and documented rather than faked closed - fixing it needs a real backend emitting real distinguishable error codes, which doesn't exist yet and can't be fabricated without inventing an API contract. No frontend code touched. No architecture changed. No APIs invented.
2026-07-26 16:10:33 +04:00
**Recommended default:** yes — revoke the whole token family on reuse of
a rotated-out refresh token (standard rotation-detection practice); this
is a security posture, not just a technical convention, so treat as
security-review-required rather than silently skippable.
- **Session/token revocation propagation** (§8) — whether/how a
remotely-revoked admin session (e.g. via the mock `AdminUsersGateway.
revokeSession`) is communicated to an already-logged-in client before its
next refresh cycle. No push/poll mechanism exists today.
docs: finalize BACKEND.md as a self-contained implementation prompt Goal: this document alone, pasted into a fresh Claude session, should be enough for a backend dev to implement against without getting stuck or inventing conventions the frontend doesn't actually need. Fixed a real bug: ~65 cross-references throughout the document pointed to docs/AUTHENTICATION.md, docs/ERROR_CONTRACT.md, docs/MAINTENANCE_MODE.md - three sibling docs that were deleted and fully merged into this document's own §4/§6/§10 during the earlier doc-consolidation pass, but the in-text references were never updated. A fresh agent following those links would hit dead ends repeatedly. Bulk-replaced with in- document section references; hand-fixed ~4 sentences that framed §6/ §10 as "sibling task, in progress" (stale - both are complete, this document's §6/§10 already are the settled contract, nothing to wait on). Added "Recommended default" to every item across the document's 5 consolidated "Requires backend decision" registers (§2.12 framework, §3.21 CRUD cross-cutting - 18 items, §4 §12 auth open items - 9 items, §6 error-model summary - 9 items, §10 maintenance-mode list - 5 items). Each default is derived from what the frontend already implies or standard REST/security convention - no invented APIs or business rules. ~6 items are explicitly flagged as real business/security decisions instead (order state-machine rules, Ed25519 cutover strategy, refresh-token reuse-detection posture) since those carry consequences no amount of frontend-code-reading can resolve. Added a "How to use this document" preamble up front: work in §9's dependency order not document order, apply recommended defaults and keep moving, only stop for the explicitly-flagged business/security items, don't invent beyond what's written or directly implied. Frontend side: confirmed nothing else is left. docs/TODO.md already has zero blockers; docs/KNOWN-ISSUES.md's one open item (unreachable Ed25519 error-code UI) is correctly left open and documented rather than faked closed - fixing it needs a real backend emitting real distinguishable error codes, which doesn't exist yet and can't be fabricated without inventing an API contract. No frontend code touched. No architecture changed. No APIs invented.
2026-07-26 16:10:33 +04:00
**Recommended default:** accept the propagation delay (§3.21 item 9) —
don't build push infrastructure for this alone.
- **Tenant scoping of auth requests** (§10) — whether login/challenge/verify
need an explicit tenant identifier in the payload, versus relying on
request origin. Not present in any current auth payload.
docs: finalize BACKEND.md as a self-contained implementation prompt Goal: this document alone, pasted into a fresh Claude session, should be enough for a backend dev to implement against without getting stuck or inventing conventions the frontend doesn't actually need. Fixed a real bug: ~65 cross-references throughout the document pointed to docs/AUTHENTICATION.md, docs/ERROR_CONTRACT.md, docs/MAINTENANCE_MODE.md - three sibling docs that were deleted and fully merged into this document's own §4/§6/§10 during the earlier doc-consolidation pass, but the in-text references were never updated. A fresh agent following those links would hit dead ends repeatedly. Bulk-replaced with in- document section references; hand-fixed ~4 sentences that framed §6/ §10 as "sibling task, in progress" (stale - both are complete, this document's §6/§10 already are the settled contract, nothing to wait on). Added "Recommended default" to every item across the document's 5 consolidated "Requires backend decision" registers (§2.12 framework, §3.21 CRUD cross-cutting - 18 items, §4 §12 auth open items - 9 items, §6 error-model summary - 9 items, §10 maintenance-mode list - 5 items). Each default is derived from what the frontend already implies or standard REST/security convention - no invented APIs or business rules. ~6 items are explicitly flagged as real business/security decisions instead (order state-machine rules, Ed25519 cutover strategy, refresh-token reuse-detection posture) since those carry consequences no amount of frontend-code-reading can resolve. Added a "How to use this document" preamble up front: work in §9's dependency order not document order, apply recommended defaults and keep moving, only stop for the explicitly-flagged business/security items, don't invent beyond what's written or directly implied. Frontend side: confirmed nothing else is left. docs/TODO.md already has zero blockers; docs/KNOWN-ISSUES.md's one open item (unreachable Ed25519 error-code UI) is correctly left open and documented rather than faked closed - fixing it needs a real backend emitting real distinguishable error codes, which doesn't exist yet and can't be fabricated without inventing an API contract. No frontend code touched. No architecture changed. No APIs invented.
2026-07-26 16:10:33 +04:00
**Recommended default:** rely on request origin/host (matches how
`ApiConfigService.getBaseUrl()` already resolves tenant elsewhere, §1.9)
rather than adding a redundant payload field.
- **Relationship between the two mechanisms at cutover** — replace
`adminAuthGuard` with `ed25519AuthGuard` outright, or run both and let
role/tenant config decide? Explicitly called out in the prior
`docs/AUTH.md` as "a product decision, not made here," and nothing has
changed that.
docs: finalize BACKEND.md as a self-contained implementation prompt Goal: this document alone, pasted into a fresh Claude session, should be enough for a backend dev to implement against without getting stuck or inventing conventions the frontend doesn't actually need. Fixed a real bug: ~65 cross-references throughout the document pointed to docs/AUTHENTICATION.md, docs/ERROR_CONTRACT.md, docs/MAINTENANCE_MODE.md - three sibling docs that were deleted and fully merged into this document's own §4/§6/§10 during the earlier doc-consolidation pass, but the in-text references were never updated. A fresh agent following those links would hit dead ends repeatedly. Bulk-replaced with in- document section references; hand-fixed ~4 sentences that framed §6/ §10 as "sibling task, in progress" (stale - both are complete, this document's §6/§10 already are the settled contract, nothing to wait on). Added "Recommended default" to every item across the document's 5 consolidated "Requires backend decision" registers (§2.12 framework, §3.21 CRUD cross-cutting - 18 items, §4 §12 auth open items - 9 items, §6 error-model summary - 9 items, §10 maintenance-mode list - 5 items). Each default is derived from what the frontend already implies or standard REST/security convention - no invented APIs or business rules. ~6 items are explicitly flagged as real business/security decisions instead (order state-machine rules, Ed25519 cutover strategy, refresh-token reuse-detection posture) since those carry consequences no amount of frontend-code-reading can resolve. Added a "How to use this document" preamble up front: work in §9's dependency order not document order, apply recommended defaults and keep moving, only stop for the explicitly-flagged business/security items, don't invent beyond what's written or directly implied. Frontend side: confirmed nothing else is left. docs/TODO.md already has zero blockers; docs/KNOWN-ISSUES.md's one open item (unreachable Ed25519 error-code UI) is correctly left open and documented rather than faked closed - fixing it needs a real backend emitting real distinguishable error codes, which doesn't exist yet and can't be fabricated without inventing an API contract. No frontend code touched. No architecture changed. No APIs invented.
2026-07-26 16:10:33 +04:00
**Business decision, not technical** — ask. (This is about auth security
posture and rollout risk, not a convention.)
- **`AdminAuthService`'s reserved JWT-pair slots** (`adminToken`/
`adminRefreshToken`, §4) — whether Mechanism A is ever meant to gain its
own token pair (as the reserved-but-unused storage suggests) independent
of the Ed25519 migration, or whether that code is dead and should be
removed. Not resolved by current usage (nothing writes to it).
docs: finalize BACKEND.md as a self-contained implementation prompt Goal: this document alone, pasted into a fresh Claude session, should be enough for a backend dev to implement against without getting stuck or inventing conventions the frontend doesn't actually need. Fixed a real bug: ~65 cross-references throughout the document pointed to docs/AUTHENTICATION.md, docs/ERROR_CONTRACT.md, docs/MAINTENANCE_MODE.md - three sibling docs that were deleted and fully merged into this document's own §4/§6/§10 during the earlier doc-consolidation pass, but the in-text references were never updated. A fresh agent following those links would hit dead ends repeatedly. Bulk-replaced with in- document section references; hand-fixed ~4 sentences that framed §6/ §10 as "sibling task, in progress" (stale - both are complete, this document's §6/§10 already are the settled contract, nothing to wait on). Added "Recommended default" to every item across the document's 5 consolidated "Requires backend decision" registers (§2.12 framework, §3.21 CRUD cross-cutting - 18 items, §4 §12 auth open items - 9 items, §6 error-model summary - 9 items, §10 maintenance-mode list - 5 items). Each default is derived from what the frontend already implies or standard REST/security convention - no invented APIs or business rules. ~6 items are explicitly flagged as real business/security decisions instead (order state-machine rules, Ed25519 cutover strategy, refresh-token reuse-detection posture) since those carry consequences no amount of frontend-code-reading can resolve. Added a "How to use this document" preamble up front: work in §9's dependency order not document order, apply recommended defaults and keep moving, only stop for the explicitly-flagged business/security items, don't invent beyond what's written or directly implied. Frontend side: confirmed nothing else is left. docs/TODO.md already has zero blockers; docs/KNOWN-ISSUES.md's one open item (unreachable Ed25519 error-code UI) is correctly left open and documented rather than faked closed - fixing it needs a real backend emitting real distinguishable error codes, which doesn't exist yet and can't be fabricated without inventing an API contract. No frontend code touched. No architecture changed. No APIs invented.
2026-07-26 16:10:33 +04:00
**Recommended default:** treat as dead/reserved-but-unused; don't build
backend support for it. Remove client-side only if a dedicated frontend
cleanup pass confirms nothing depends on the reserved slots existing.
- **`AdminRole` naming collision** (§9) — a reconciliation/rename decision
between `core/auth/models/permission.model.ts`'s string union and
`features/admin/users/models/admin-user.model.ts`'s interface; flagged,
not resolved, by this document.
docs: finalize BACKEND.md as a self-contained implementation prompt Goal: this document alone, pasted into a fresh Claude session, should be enough for a backend dev to implement against without getting stuck or inventing conventions the frontend doesn't actually need. Fixed a real bug: ~65 cross-references throughout the document pointed to docs/AUTHENTICATION.md, docs/ERROR_CONTRACT.md, docs/MAINTENANCE_MODE.md - three sibling docs that were deleted and fully merged into this document's own §4/§6/§10 during the earlier doc-consolidation pass, but the in-text references were never updated. A fresh agent following those links would hit dead ends repeatedly. Bulk-replaced with in- document section references; hand-fixed ~4 sentences that framed §6/ §10 as "sibling task, in progress" (stale - both are complete, this document's §6/§10 already are the settled contract, nothing to wait on). Added "Recommended default" to every item across the document's 5 consolidated "Requires backend decision" registers (§2.12 framework, §3.21 CRUD cross-cutting - 18 items, §4 §12 auth open items - 9 items, §6 error-model summary - 9 items, §10 maintenance-mode list - 5 items). Each default is derived from what the frontend already implies or standard REST/security convention - no invented APIs or business rules. ~6 items are explicitly flagged as real business/security decisions instead (order state-machine rules, Ed25519 cutover strategy, refresh-token reuse-detection posture) since those carry consequences no amount of frontend-code-reading can resolve. Added a "How to use this document" preamble up front: work in §9's dependency order not document order, apply recommended defaults and keep moving, only stop for the explicitly-flagged business/security items, don't invent beyond what's written or directly implied. Frontend side: confirmed nothing else is left. docs/TODO.md already has zero blockers; docs/KNOWN-ISSUES.md's one open item (unreachable Ed25519 error-code UI) is correctly left open and documented rather than faked closed - fixing it needs a real backend emitting real distinguishable error codes, which doesn't exist yet and can't be fabricated without inventing an API contract. No frontend code touched. No architecture changed. No APIs invented.
2026-07-26 16:10:33 +04:00
**Recommended default:** same as §3.21 item 8 — keep the coarse string
union as the enforced source, treat the richer interface as display-only.
- **Fine-grained/per-domain permissions** (§9.1) — the current model is
intentionally coarse; whether a richer permission model is ever needed is
a backend/product decision.
docs: finalize BACKEND.md as a self-contained implementation prompt Goal: this document alone, pasted into a fresh Claude session, should be enough for a backend dev to implement against without getting stuck or inventing conventions the frontend doesn't actually need. Fixed a real bug: ~65 cross-references throughout the document pointed to docs/AUTHENTICATION.md, docs/ERROR_CONTRACT.md, docs/MAINTENANCE_MODE.md - three sibling docs that were deleted and fully merged into this document's own §4/§6/§10 during the earlier doc-consolidation pass, but the in-text references were never updated. A fresh agent following those links would hit dead ends repeatedly. Bulk-replaced with in- document section references; hand-fixed ~4 sentences that framed §6/ §10 as "sibling task, in progress" (stale - both are complete, this document's §6/§10 already are the settled contract, nothing to wait on). Added "Recommended default" to every item across the document's 5 consolidated "Requires backend decision" registers (§2.12 framework, §3.21 CRUD cross-cutting - 18 items, §4 §12 auth open items - 9 items, §6 error-model summary - 9 items, §10 maintenance-mode list - 5 items). Each default is derived from what the frontend already implies or standard REST/security convention - no invented APIs or business rules. ~6 items are explicitly flagged as real business/security decisions instead (order state-machine rules, Ed25519 cutover strategy, refresh-token reuse-detection posture) since those carry consequences no amount of frontend-code-reading can resolve. Added a "How to use this document" preamble up front: work in §9's dependency order not document order, apply recommended defaults and keep moving, only stop for the explicitly-flagged business/security items, don't invent beyond what's written or directly implied. Frontend side: confirmed nothing else is left. docs/TODO.md already has zero blockers; docs/KNOWN-ISSUES.md's one open item (unreachable Ed25519 error-code UI) is correctly left open and documented rather than faked closed - fixing it needs a real backend emitting real distinguishable error codes, which doesn't exist yet and can't be fabricated without inventing an API contract. No frontend code touched. No architecture changed. No APIs invented.
2026-07-26 16:10:33 +04:00
**Recommended default:** keep it coarse (`backoffice.read`/
`backoffice.write`/`builder.read`/`builder.write`/`settings.manage`) —
don't build fine-grained permissions speculatively; the frontend has no
UI that would use finer granularity today.
- **`APP_INITIALIZER` wiring for `AuthFacade.restoreSession()`** (§5.2) — the
code comment says this should be wired in before the Ed25519 flow goes
live, but it is not wired in today. This is frontend follow-up work, not a
backend decision, but is listed here because it changes what "session
restored on refresh" means in practice until it lands.
docs: finalize BACKEND.md as a self-contained implementation prompt Goal: this document alone, pasted into a fresh Claude session, should be enough for a backend dev to implement against without getting stuck or inventing conventions the frontend doesn't actually need. Fixed a real bug: ~65 cross-references throughout the document pointed to docs/AUTHENTICATION.md, docs/ERROR_CONTRACT.md, docs/MAINTENANCE_MODE.md - three sibling docs that were deleted and fully merged into this document's own §4/§6/§10 during the earlier doc-consolidation pass, but the in-text references were never updated. A fresh agent following those links would hit dead ends repeatedly. Bulk-replaced with in- document section references; hand-fixed ~4 sentences that framed §6/ §10 as "sibling task, in progress" (stale - both are complete, this document's §6/§10 already are the settled contract, nothing to wait on). Added "Recommended default" to every item across the document's 5 consolidated "Requires backend decision" registers (§2.12 framework, §3.21 CRUD cross-cutting - 18 items, §4 §12 auth open items - 9 items, §6 error-model summary - 9 items, §10 maintenance-mode list - 5 items). Each default is derived from what the frontend already implies or standard REST/security convention - no invented APIs or business rules. ~6 items are explicitly flagged as real business/security decisions instead (order state-machine rules, Ed25519 cutover strategy, refresh-token reuse-detection posture) since those carry consequences no amount of frontend-code-reading can resolve. Added a "How to use this document" preamble up front: work in §9's dependency order not document order, apply recommended defaults and keep moving, only stop for the explicitly-flagged business/security items, don't invent beyond what's written or directly implied. Frontend side: confirmed nothing else is left. docs/TODO.md already has zero blockers; docs/KNOWN-ISSUES.md's one open item (unreachable Ed25519 error-code UI) is correctly left open and documented rather than faked closed - fixing it needs a real backend emitting real distinguishable error codes, which doesn't exist yet and can't be fabricated without inventing an API contract. No frontend code touched. No architecture changed. No APIs invented.
2026-07-26 16:10:33 +04:00
**Recommended default:** frontend must wire this before Ed25519 goes live
in production — flag as a blocking frontend task in the cutover plan, not
something backend needs to wait on.
---
## 5. Security
Every claim below is grounded in current source on branch `B2B`. Where the
frontend implies nothing, the item is marked **Requires backend decision**.
docs: finalize BACKEND.md as a self-contained implementation prompt Goal: this document alone, pasted into a fresh Claude session, should be enough for a backend dev to implement against without getting stuck or inventing conventions the frontend doesn't actually need. Fixed a real bug: ~65 cross-references throughout the document pointed to docs/AUTHENTICATION.md, docs/ERROR_CONTRACT.md, docs/MAINTENANCE_MODE.md - three sibling docs that were deleted and fully merged into this document's own §4/§6/§10 during the earlier doc-consolidation pass, but the in-text references were never updated. A fresh agent following those links would hit dead ends repeatedly. Bulk-replaced with in- document section references; hand-fixed ~4 sentences that framed §6/ §10 as "sibling task, in progress" (stale - both are complete, this document's §6/§10 already are the settled contract, nothing to wait on). Added "Recommended default" to every item across the document's 5 consolidated "Requires backend decision" registers (§2.12 framework, §3.21 CRUD cross-cutting - 18 items, §4 §12 auth open items - 9 items, §6 error-model summary - 9 items, §10 maintenance-mode list - 5 items). Each default is derived from what the frontend already implies or standard REST/security convention - no invented APIs or business rules. ~6 items are explicitly flagged as real business/security decisions instead (order state-machine rules, Ed25519 cutover strategy, refresh-token reuse-detection posture) since those carry consequences no amount of frontend-code-reading can resolve. Added a "How to use this document" preamble up front: work in §9's dependency order not document order, apply recommended defaults and keep moving, only stop for the explicitly-flagged business/security items, don't invent beyond what's written or directly implied. Frontend side: confirmed nothing else is left. docs/TODO.md already has zero blockers; docs/KNOWN-ISSUES.md's one open item (unreachable Ed25519 error-code UI) is correctly left open and documented rather than faked closed - fixing it needs a real backend emitting real distinguishable error codes, which doesn't exist yet and can't be fabricated without inventing an API contract. No frontend code touched. No architecture changed. No APIs invented.
2026-07-26 16:10:33 +04:00
Auth and error contracts are owned by §4 and §6 respectively — this section
references them rather than restating them, and only covers the
security-relevant angle.
### 5.1 Origin
The frontend calls a small, fixed set of origins, all declared in
`src/environments/environment.ts` / `environment.production.ts`:
| Origin | Source key | Used for |
|---|---|---|
| `https://api.dexarmarket.ru:445` | `tenantApiBaseUrls.default`, `authApiUrl`, and prod `apiUrl`/`localhostApiUrl` | marketplace API + session auth + Ed25519 admin auth |
| `https://{tenant}.api.dexarmarket.ru:445` | `tenantApiTemplate` | per-tenant marketplace API (subdomain-substituted) |
| `https://qr.vitanova.network/api` | `qrApiUrl` | payment / QR / card status |
| `/api` (relative) | dev `apiUrl`/`localhostApiUrl` | localhost, proxied by nginx per tenant |
| `https://api.qrserver.com/...` | literal in `ApiService` | QR **bitmap** image only (not a platform backend) |
| `http://ip-api.com/json/...` | literal in `LocationService` | external geo-IP autodetect (no key, plaintext HTTP) |
The tenant marketplace origin is chosen by **subdomain** (`TenantResolverService`,
docs: finalize BACKEND.md as a self-contained implementation prompt Goal: this document alone, pasted into a fresh Claude session, should be enough for a backend dev to implement against without getting stuck or inventing conventions the frontend doesn't actually need. Fixed a real bug: ~65 cross-references throughout the document pointed to docs/AUTHENTICATION.md, docs/ERROR_CONTRACT.md, docs/MAINTENANCE_MODE.md - three sibling docs that were deleted and fully merged into this document's own §4/§6/§10 during the earlier doc-consolidation pass, but the in-text references were never updated. A fresh agent following those links would hit dead ends repeatedly. Bulk-replaced with in- document section references; hand-fixed ~4 sentences that framed §6/ §10 as "sibling task, in progress" (stale - both are complete, this document's §6/§10 already are the settled contract, nothing to wait on). Added "Recommended default" to every item across the document's 5 consolidated "Requires backend decision" registers (§2.12 framework, §3.21 CRUD cross-cutting - 18 items, §4 §12 auth open items - 9 items, §6 error-model summary - 9 items, §10 maintenance-mode list - 5 items). Each default is derived from what the frontend already implies or standard REST/security convention - no invented APIs or business rules. ~6 items are explicitly flagged as real business/security decisions instead (order state-machine rules, Ed25519 cutover strategy, refresh-token reuse-detection posture) since those carry consequences no amount of frontend-code-reading can resolve. Added a "How to use this document" preamble up front: work in §9's dependency order not document order, apply recommended defaults and keep moving, only stop for the explicitly-flagged business/security items, don't invent beyond what's written or directly implied. Frontend side: confirmed nothing else is left. docs/TODO.md already has zero blockers; docs/KNOWN-ISSUES.md's one open item (unreachable Ed25519 error-code UI) is correctly left open and documented rather than faked closed - fixing it needs a real backend emitting real distinguishable error codes, which doesn't exist yet and can't be fabricated without inventing an API contract. No frontend code touched. No architecture changed. No APIs invented.
2026-07-26 16:10:33 +04:00
see `§4 (Authentication, this document)` §10) — there is no `X-Tenant` header or tenant
path prefix. **Backend note:** the browser will make cross-origin requests to
`api.dexarmarket.ru:445` and `qr.vitanova.network` from whatever host the SPA
is served on (e.g. `dexarmarket.ru`), so those origins must return correct
CORS headers (§5.2). `ip-api.com` is called over plaintext HTTP — a mixed-content
and privacy concern flagged here; the frontend already tolerates its failure
(falls back to 6 hardcoded regions).
### 5.2 CORS
**Bearer/header-only, never credentialed.** No `HttpClient` call in the
codebase sets `withCredentials: true` (grep: zero matches in `src/`). Identity
travels in **headers**, not cookies:
- `WebSessionID` on every marketplace API request (`apiHeadersInterceptor`).
- `AdminWebSessionID` + `Authorization: Bearer <token>` on `/admin/`,
`/backoffice/`, `/builder/`, `/media/` requests (`adminAuthHeadersInterceptor`).
- `authorization-key` / `userid-value` on QR payment calls.
The auth **cookies** (`webSessionID`, `adminSessionID`) are same-site,
first-party, and are **not** sent cross-origin to the API — they exist only so
the SPA can restore its own session locally. **Backend requirement:** the API
must accept these requests as CORS with `Access-Control-Allow-Origin` set to
the specific SPA origin(s) and `Access-Control-Allow-Headers` including
`WebSessionID, AdminWebSessionID, Authorization, X-Region, X-Language, Currency`
(and the QR API's `authorization-key, userid-value`). Because no request is
credentialed, `Access-Control-Allow-Credentials` is **not** required and
`Allow-Origin` may be specific per tenant.
### 5.3 CSRF
**Not applicable to the API surface, by construction.** All API authentication
is bearer/header-based (§5.2) with **no cookie ever transmitted to the API**
and `withCredentials` never set. A CSRF attack relies on the browser
auto-attaching an ambient credential (cookie) to a forged cross-site request;
since the API authenticates on a header the attacker's page cannot set
cross-origin, a classic CSRF POST cannot forge an authenticated call. There is
zero CSRF-token handling in the frontend (no XSRF interceptor, no
`X-CSRF-Token`/`X-XSRF-TOKEN` reference anywhere in `src/`), and none is
needed for the current design. **Backend note:** this guarantee holds only as
long as the API does **not** start accepting a cookie as an auth credential —
if a future endpoint authenticates via cookie, CSRF protection becomes
mandatory. Requires backend decision only if that design change is made.
### 5.4 JWT
docs: finalize BACKEND.md as a self-contained implementation prompt Goal: this document alone, pasted into a fresh Claude session, should be enough for a backend dev to implement against without getting stuck or inventing conventions the frontend doesn't actually need. Fixed a real bug: ~65 cross-references throughout the document pointed to docs/AUTHENTICATION.md, docs/ERROR_CONTRACT.md, docs/MAINTENANCE_MODE.md - three sibling docs that were deleted and fully merged into this document's own §4/§6/§10 during the earlier doc-consolidation pass, but the in-text references were never updated. A fresh agent following those links would hit dead ends repeatedly. Bulk-replaced with in- document section references; hand-fixed ~4 sentences that framed §6/ §10 as "sibling task, in progress" (stale - both are complete, this document's §6/§10 already are the settled contract, nothing to wait on). Added "Recommended default" to every item across the document's 5 consolidated "Requires backend decision" registers (§2.12 framework, §3.21 CRUD cross-cutting - 18 items, §4 §12 auth open items - 9 items, §6 error-model summary - 9 items, §10 maintenance-mode list - 5 items). Each default is derived from what the frontend already implies or standard REST/security convention - no invented APIs or business rules. ~6 items are explicitly flagged as real business/security decisions instead (order state-machine rules, Ed25519 cutover strategy, refresh-token reuse-detection posture) since those carry consequences no amount of frontend-code-reading can resolve. Added a "How to use this document" preamble up front: work in §9's dependency order not document order, apply recommended defaults and keep moving, only stop for the explicitly-flagged business/security items, don't invent beyond what's written or directly implied. Frontend side: confirmed nothing else is left. docs/TODO.md already has zero blockers; docs/KNOWN-ISSUES.md's one open item (unreachable Ed25519 error-code UI) is correctly left open and documented rather than faked closed - fixing it needs a real backend emitting real distinguishable error codes, which doesn't exist yet and can't be fabricated without inventing an API contract. No frontend code touched. No architecture changed. No APIs invented.
2026-07-26 16:10:33 +04:00
Cross-reference `§4 (Authentication, this document)` §3§4. Security-relevant storage
angle only:
- **Live today (Telegram session, Mechanism A):** no JWT at all — an opaque
`webSessionID` in a cookie + in-memory signal. The reserved
`localStorage['adminToken']` / `adminRefreshToken` slots are **dead** (nothing
writes them).
- **Ed25519 flow (Mechanism B, dormant):** access + refresh tokens live in
`localStorage` (`ed25519AdminToken`, `ed25519AdminRefreshToken`).
**XSS exposure implication:** `localStorage` is readable by any script in the
origin, so a single XSS foothold (see §5.8) exfiltrates the admin token pair
outright — there is no `HttpOnly` cookie protecting them. This is the
standard SPA trade-off and is acceptable **only** if the XSS surface (§5.8)
is genuinely closed server-side. **Requires backend decision:** whether admin
tokens should instead be delivered as `HttpOnly; Secure; SameSite` cookies
to remove the XSS-exfiltration path — the frontend currently assumes
`localStorage`, so this would be a coordinated frontend+backend change.
### 5.5 Refresh
Refresh tokens are protected client-side only as well as `localStorage`
protects them — i.e. not from XSS (§5.4). The frontend never decodes the
refresh token (round-tripped opaque), stores it beside the access token, and
docs: finalize BACKEND.md as a self-contained implementation prompt Goal: this document alone, pasted into a fresh Claude session, should be enough for a backend dev to implement against without getting stuck or inventing conventions the frontend doesn't actually need. Fixed a real bug: ~65 cross-references throughout the document pointed to docs/AUTHENTICATION.md, docs/ERROR_CONTRACT.md, docs/MAINTENANCE_MODE.md - three sibling docs that were deleted and fully merged into this document's own §4/§6/§10 during the earlier doc-consolidation pass, but the in-text references were never updated. A fresh agent following those links would hit dead ends repeatedly. Bulk-replaced with in- document section references; hand-fixed ~4 sentences that framed §6/ §10 as "sibling task, in progress" (stale - both are complete, this document's §6/§10 already are the settled contract, nothing to wait on). Added "Recommended default" to every item across the document's 5 consolidated "Requires backend decision" registers (§2.12 framework, §3.21 CRUD cross-cutting - 18 items, §4 §12 auth open items - 9 items, §6 error-model summary - 9 items, §10 maintenance-mode list - 5 items). Each default is derived from what the frontend already implies or standard REST/security convention - no invented APIs or business rules. ~6 items are explicitly flagged as real business/security decisions instead (order state-machine rules, Ed25519 cutover strategy, refresh-token reuse-detection posture) since those carry consequences no amount of frontend-code-reading can resolve. Added a "How to use this document" preamble up front: work in §9's dependency order not document order, apply recommended defaults and keep moving, only stop for the explicitly-flagged business/security items, don't invent beyond what's written or directly implied. Frontend side: confirmed nothing else is left. docs/TODO.md already has zero blockers; docs/KNOWN-ISSUES.md's one open item (unreachable Ed25519 error-code UI) is correctly left open and documented rather than faked closed - fixing it needs a real backend emitting real distinguishable error codes, which doesn't exist yet and can't be fabricated without inventing an API contract. No frontend code touched. No architecture changed. No APIs invented.
2026-07-26 16:10:33 +04:00
replaces the pair on every `/verify` and `/refresh` (`§4 (Authentication, this document)`
§4, §6). Rotation is **expected** but only the backend can enforce it. There is
no client-side reuse-detection. **Requires backend decision** (already flagged
docs: finalize BACKEND.md as a self-contained implementation prompt Goal: this document alone, pasted into a fresh Claude session, should be enough for a backend dev to implement against without getting stuck or inventing conventions the frontend doesn't actually need. Fixed a real bug: ~65 cross-references throughout the document pointed to docs/AUTHENTICATION.md, docs/ERROR_CONTRACT.md, docs/MAINTENANCE_MODE.md - three sibling docs that were deleted and fully merged into this document's own §4/§6/§10 during the earlier doc-consolidation pass, but the in-text references were never updated. A fresh agent following those links would hit dead ends repeatedly. Bulk-replaced with in- document section references; hand-fixed ~4 sentences that framed §6/ §10 as "sibling task, in progress" (stale - both are complete, this document's §6/§10 already are the settled contract, nothing to wait on). Added "Recommended default" to every item across the document's 5 consolidated "Requires backend decision" registers (§2.12 framework, §3.21 CRUD cross-cutting - 18 items, §4 §12 auth open items - 9 items, §6 error-model summary - 9 items, §10 maintenance-mode list - 5 items). Each default is derived from what the frontend already implies or standard REST/security convention - no invented APIs or business rules. ~6 items are explicitly flagged as real business/security decisions instead (order state-machine rules, Ed25519 cutover strategy, refresh-token reuse-detection posture) since those carry consequences no amount of frontend-code-reading can resolve. Added a "How to use this document" preamble up front: work in §9's dependency order not document order, apply recommended defaults and keep moving, only stop for the explicitly-flagged business/security items, don't invent beyond what's written or directly implied. Frontend side: confirmed nothing else is left. docs/TODO.md already has zero blockers; docs/KNOWN-ISSUES.md's one open item (unreachable Ed25519 error-code UI) is correctly left open and documented rather than faked closed - fixing it needs a real backend emitting real distinguishable error codes, which doesn't exist yet and can't be fabricated without inventing an API contract. No frontend code touched. No architecture changed. No APIs invented.
2026-07-26 16:10:33 +04:00
in `§4 (Authentication, this document)` §6): single-use refresh tokens + reuse/compromise
revocation cascade — nothing in the frontend implies or depends on it.
### 5.6 Rate limiting
There is **no client-side rate-limit handling** (grep: no `429` / "rate limit"
docs: finalize BACKEND.md as a self-contained implementation prompt Goal: this document alone, pasted into a fresh Claude session, should be enough for a backend dev to implement against without getting stuck or inventing conventions the frontend doesn't actually need. Fixed a real bug: ~65 cross-references throughout the document pointed to docs/AUTHENTICATION.md, docs/ERROR_CONTRACT.md, docs/MAINTENANCE_MODE.md - three sibling docs that were deleted and fully merged into this document's own §4/§6/§10 during the earlier doc-consolidation pass, but the in-text references were never updated. A fresh agent following those links would hit dead ends repeatedly. Bulk-replaced with in- document section references; hand-fixed ~4 sentences that framed §6/ §10 as "sibling task, in progress" (stale - both are complete, this document's §6/§10 already are the settled contract, nothing to wait on). Added "Recommended default" to every item across the document's 5 consolidated "Requires backend decision" registers (§2.12 framework, §3.21 CRUD cross-cutting - 18 items, §4 §12 auth open items - 9 items, §6 error-model summary - 9 items, §10 maintenance-mode list - 5 items). Each default is derived from what the frontend already implies or standard REST/security convention - no invented APIs or business rules. ~6 items are explicitly flagged as real business/security decisions instead (order state-machine rules, Ed25519 cutover strategy, refresh-token reuse-detection posture) since those carry consequences no amount of frontend-code-reading can resolve. Added a "How to use this document" preamble up front: work in §9's dependency order not document order, apply recommended defaults and keep moving, only stop for the explicitly-flagged business/security items, don't invent beyond what's written or directly implied. Frontend side: confirmed nothing else is left. docs/TODO.md already has zero blockers; docs/KNOWN-ISSUES.md's one open item (unreachable Ed25519 error-code UI) is correctly left open and documented rather than faked closed - fixing it needs a real backend emitting real distinguishable error codes, which doesn't exist yet and can't be fabricated without inventing an API contract. No frontend code touched. No architecture changed. No APIs invented.
2026-07-26 16:10:33 +04:00
reference in `src/`; confirmed by `§6 (Error Model, this document)` §429). The frontend
does, however, apply **debounce** on user-driven request bursts, which implies
the natural request cadence a backend limiter should tolerate rather than block:
- Search-as-you-type: `debounceTime(220)` in `SearchFacade`
(`src/app/features/search/facade/search.facade.ts:98`) — so a typing user
produces at most ~45 `GET /searchitems` calls/second, not one per keystroke.
- Project-editor autosave: ~300 ms debounce before a draft commit
(localStorage only, not a backend call today).
**Requires backend decision** on the rate-limit contract end to end (limits,
`Retry-After` header vs `retryAfterSeconds` body field, and whether the
docs: finalize BACKEND.md as a self-contained implementation prompt Goal: this document alone, pasted into a fresh Claude session, should be enough for a backend dev to implement against without getting stuck or inventing conventions the frontend doesn't actually need. Fixed a real bug: ~65 cross-references throughout the document pointed to docs/AUTHENTICATION.md, docs/ERROR_CONTRACT.md, docs/MAINTENANCE_MODE.md - three sibling docs that were deleted and fully merged into this document's own §4/§6/§10 during the earlier doc-consolidation pass, but the in-text references were never updated. A fresh agent following those links would hit dead ends repeatedly. Bulk-replaced with in- document section references; hand-fixed ~4 sentences that framed §6/ §10 as "sibling task, in progress" (stale - both are complete, this document's §6/§10 already are the settled contract, nothing to wait on). Added "Recommended default" to every item across the document's 5 consolidated "Requires backend decision" registers (§2.12 framework, §3.21 CRUD cross-cutting - 18 items, §4 §12 auth open items - 9 items, §6 error-model summary - 9 items, §10 maintenance-mode list - 5 items). Each default is derived from what the frontend already implies or standard REST/security convention - no invented APIs or business rules. ~6 items are explicitly flagged as real business/security decisions instead (order state-machine rules, Ed25519 cutover strategy, refresh-token reuse-detection posture) since those carry consequences no amount of frontend-code-reading can resolve. Added a "How to use this document" preamble up front: work in §9's dependency order not document order, apply recommended defaults and keep moving, only stop for the explicitly-flagged business/security items, don't invent beyond what's written or directly implied. Frontend side: confirmed nothing else is left. docs/TODO.md already has zero blockers; docs/KNOWN-ISSUES.md's one open item (unreachable Ed25519 error-code UI) is correctly left open and documented rather than faked closed - fixing it needs a real backend emitting real distinguishable error codes, which doesn't exist yet and can't be fabricated without inventing an API contract. No frontend code touched. No architecture changed. No APIs invented.
2026-07-26 16:10:33 +04:00
frontend should auto-retry). Per `§6 (Error Model, this document)` §429, no retry
interceptor exists — honoring 429 is net-new frontend work, not a config
change.
### 5.7 Replay protection
docs: finalize BACKEND.md as a self-contained implementation prompt Goal: this document alone, pasted into a fresh Claude session, should be enough for a backend dev to implement against without getting stuck or inventing conventions the frontend doesn't actually need. Fixed a real bug: ~65 cross-references throughout the document pointed to docs/AUTHENTICATION.md, docs/ERROR_CONTRACT.md, docs/MAINTENANCE_MODE.md - three sibling docs that were deleted and fully merged into this document's own §4/§6/§10 during the earlier doc-consolidation pass, but the in-text references were never updated. A fresh agent following those links would hit dead ends repeatedly. Bulk-replaced with in- document section references; hand-fixed ~4 sentences that framed §6/ §10 as "sibling task, in progress" (stale - both are complete, this document's §6/§10 already are the settled contract, nothing to wait on). Added "Recommended default" to every item across the document's 5 consolidated "Requires backend decision" registers (§2.12 framework, §3.21 CRUD cross-cutting - 18 items, §4 §12 auth open items - 9 items, §6 error-model summary - 9 items, §10 maintenance-mode list - 5 items). Each default is derived from what the frontend already implies or standard REST/security convention - no invented APIs or business rules. ~6 items are explicitly flagged as real business/security decisions instead (order state-machine rules, Ed25519 cutover strategy, refresh-token reuse-detection posture) since those carry consequences no amount of frontend-code-reading can resolve. Added a "How to use this document" preamble up front: work in §9's dependency order not document order, apply recommended defaults and keep moving, only stop for the explicitly-flagged business/security items, don't invent beyond what's written or directly implied. Frontend side: confirmed nothing else is left. docs/TODO.md already has zero blockers; docs/KNOWN-ISSUES.md's one open item (unreachable Ed25519 error-code UI) is correctly left open and documented rather than faked closed - fixing it needs a real backend emitting real distinguishable error codes, which doesn't exist yet and can't be fabricated without inventing an API contract. No frontend code touched. No architecture changed. No APIs invented.
2026-07-26 16:10:33 +04:00
The Ed25519 signing flow (`§4 (Authentication, this document)` §2) signs the **raw
backend-issued nonce string exactly as received** — `Ed25519KeypairService.sign(nonce)`
applies no client-side framing, prefix, hashing, timestamp, or
counter (`src/app/core/auth/services/auth.service.ts` login flow;
`ed25519-keypair.service.ts`). The `VerifySignatureRequest` body is
`{ publicKey, signature, nonce }` and nothing else. Therefore **all** replay
protection is the backend's responsibility via the nonce it issues:
`AuthChallenge { nonce, issuedAt, expiresAt }`. **Backend requirement:** treat
each nonce as single-use, bind it to the issuing client, reject it after
`expiresAt`, and reject any reuse. The frontend contributes no independent
freshness signal — there is no client timestamp or per-request nonce on any
other call either (the Telegram session flow has no signing at all).
### 5.8 XSS
The frontend renders **backend/CMS-controlled HTML** in three places — this is
the single most important input for what the backend must sanitize:
| Location | File | Handling today |
|---|---|---|
| Storefront static page body (CMS content) | `src/app/pages/static-page/static-page.component.ts:104-106` | Runs `DomSanitizer.sanitize(SecurityContext.HTML, html)` **first**, then `bypassSecurityTrustHtml(sanitized)`. So it is Angular-sanitized before injection into `[innerHTML]`. |
| Static-page **preview** (editor) | `src/app/features/content-management/components/static-page-preview/static-page-preview.component.ts:49` | `bypassSecurityTrustHtml(sanitized)` after a sanitize pass. |
| Payment bank-frame URL | `src/app/pages/cart/cart.component.ts:475` | `bypassSecurityTrustResourceUrl(bankUrl)` — trusts a backend-supplied iframe URL. |
The static-page path is the risky one: CMS body HTML originates from the
bootstrap document (`BootstrapConfig.staticPages`, backend-served). It **is**
run through Angular's sanitizer client-side, but Angular sanitization strips
active content for the DOM context only — it is **not** a substitute for
server-side sanitization/storage validation. **Backend requirement:** treat all
CMS static-page HTML and any product-description HTML as untrusted on write,
sanitize/allowlist server-side, and never assume the client sanitizer is the
only defense. The `bypassSecurityTrustResourceUrl` on the bank payment frame
means the backend-supplied payment URL must be strictly validated server-side
(a malicious value renders in an iframe with `ResourceUrl` trust). Product
descriptions are currently normalized to plain strings by
`ApiService.normalizeItem` and are **not** rendered via `[innerHTML]` today, so
they are lower-risk — but that holds only while descriptions stay plain-text.
Note: the project-editor HTML editor (`marketplace-html-editor.component.ts`)
uses `innerHTML` on a `contenteditable` surface for authoring; that is
author-local editor state, not backend-rendered content, so it is out of the
backend's sanitization scope.
### 5.9 CSP
**No CSP meta tag in `src/index.html`** (confirmed — only SEO/OG/Twitter/theme
meta tags are present). CSP is delivered **at the web-server layer** via
`nginx.conf`, and only on the primary tenant block:
- `dexarmarket.ru` server block (`nginx.conf:41`) sets a full
`Content-Security-Policy`:
`default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://telegram.org; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; font-src 'self' https://fonts.gstatic.com; img-src 'self' data: https:; connect-src 'self' https:; frame-src https://telegram.org;`
plus `X-Frame-Options: SAMEORIGIN`, `X-Content-Type-Options: nosniff`,
`X-XSS-Protection: 1; mode=block`, `Referrer-Policy: strict-origin-when-cross-origin`,
`Permissions-Policy: camera=(), microphone=(), geolocation=()`.
- **The `lovero.store` block and the new-tenant template block set the other
security headers but omit the `Content-Security-Policy` header entirely** —
an inconsistency the backend/ops team should reconcile so every tenant gets
the same CSP.
**Backend/ops observations:** the current CSP is permissive — `script-src`
allows `'unsafe-inline'` and `'unsafe-eval'`, `connect-src 'self' https:`
allows connections to any HTTPS origin, and `img-src` allows any `https:` and
`data:`. This weakens the XSS defense-in-depth that would otherwise back up
§5.8. **Requires backend decision:** whether to tighten the CSP (drop
`unsafe-inline`/`unsafe-eval`, pin `connect-src` to the known API origins in
§5.1) and whether to apply it uniformly across all tenant server blocks.
### 5.10 Upload validation (client-side, in the media picker/library)
The only upload path is `MockMediaRepository.upload()`
(`src/app/core/media/mock-media-repository.service.ts`). Before storing, it
runs `validateFile(file)`:
- **Size:** rejects if `file.size > MAX_FILE_SIZE_BYTES` where
`MAX_FILE_SIZE_BYTES = 10 * 1024 * 1024` (**10 MB**, line 9).
- **Type:** rejects if `file.type` is not in `ALLOWED_MIME_TYPES`
(line 12) — see §5.12 for the exact list.
It then transforms before storage: raster images are downscaled to
`MAX_IMAGE_DIMENSION = 2000` px on the longest edge at `COMPRESS_QUALITY = 0.85`
(canvas re-encode to JPEG/PNG); GIFs pass through untouched; **SVGs are
sanitized** (§5.11). Validation is keyed on `file.type` (the browser-reported
MIME), which is trivially spoofable — see §5.11. The `<input type=file>` in the
media picker does not set an `accept` attribute filtering the OS picker, so the
MIME check is the only gate.
### 5.11 Media validation (backend-side expectations)
Because the client checks above run in the browser and can be bypassed
(direct API call, forged `Content-Type`, renamed file), the backend must
**re-validate everything independently**:
- **Re-derive the real content type** from magic bytes / content sniffing, not
the client-declared MIME or filename extension. `assetKind()` client-side
keys purely off the declared MIME string.
- **Re-enforce the size limit** server-side (10 MB is the frontend's number,
§5.10 / §5.12 — the backend may set its own, but must enforce one).
- **Re-sanitize SVGs.** The frontend's `sanitizeSvg()` only strips `<script>`
elements and `on*="..."` inline event-handler attributes via regex
(lines 124-131). This is **naive** — it misses `<foreignObject>`,
`javascript:` URIs in `href`/`xlink:href`, CSS-based vectors, and namespaced
handlers. SVG is served with `image/svg+xml` and can execute script when
loaded as a document, so the backend must run a real SVG sanitizer/allowlist,
never trust the client's regex pass.
- **Strip/normalize metadata** (EXIF, embedded payloads) on ingest.
- **Serve uploaded assets from an isolated origin / with `Content-Disposition`
and `X-Content-Type-Options: nosniff`** so a malicious file can't be
interpreted as active content on the app origin.
### 5.12 Maximum file sizes
Exact client-enforced constant: **10 MB**
`MAX_FILE_SIZE_BYTES = 10 * 1024 * 1024`
(`mock-media-repository.service.ts:9`). The user-facing error is
`"File exceeds the 10MB limit."`. Related transform constants (not rejection
limits): `MAX_IMAGE_DIMENSION = 2000` px, `COMPRESS_QUALITY = 0.85`. No
per-account or aggregate storage quota is enforced client-side — see §7 Limits.
### 5.13 Mime validation
Exact client-accepted MIME allowlist
(`ALLOWED_MIME_TYPES`, `mock-media-repository.service.ts:12`):
```
image/jpeg, image/png, image/webp, image/gif, image/svg+xml, application/pdf
```
These map to the `MediaAssetKind` union (`image` | `svg` | `pdf` | `other`)
via `assetKind()`. The media picker's type filter exposes only
`image / svg / pdf` to the user. No extension allowlist exists separately — the
check is MIME-string only. The backend allowlist should mirror this set (and
re-derive type from content, §5.11).
### 5.14 Audit logging
The admin **Monitoring** feature defines an event taxonomy the backend is
expected to populate. `AdminMonitoringCategory`
(`src/app/features/admin/monitoring/models/admin-monitoring.model.ts:1`) is:
```
'audit' | 'security' | 'login' | 'failed_login' | 'api' | 'error' | 'warning'
```
Each `AdminMonitoringEvent` carries `{ id, category, level (info|warning|error),
message, technicalDetail?, actor, timestamp }`. The page filters events by these
categories (`admin-monitoring-page.component.ts:26`). This is **MOCK-ONLY today**
(no backend seam — see `docs/context/BACKEND-AUDIT.md` §14), but it defines the
**event types the backend audit log must eventually emit** for the UI to be
meaningful:
- `audit` — admin actions taken (create/update/delete on any admin domain).
- `security` — security-relevant events (permission changes, revocations).
- `login` / `failed_login` — successful and failed authentication attempts,
with an `actor` identity.
- `api` / `error` / `warning` — request-level diagnostics.
Additionally, `AdminUserAuditEntry`
(`src/app/features/admin/users/models/admin-user.model.ts:43`) implies a
per-user audit trail of `roleChanged` / `statusChanged` events with `actor` and
`timestamp`. **Backend requirement:** every admin mutation and auth event must
be recorded with actor + timestamp so these two audit surfaces have real data;
each event needs a stable `category`/`eventKey`, a human `message`, and an
optional `technicalDetail`. **Requires backend decision:** retention window,
whether failed-login events capture source IP/device (the `AdminSession` model
carries `device`/`ip`, so the frontend already anticipates it), and the exact
action→category mapping.
### 5.15 Permission matrix (role × domain × actions)
**Critical finding first:** the **live** admin gate does **no role check at
all**. `adminAuthGuard` (`src/app/core/admin-auth/admin-auth.guard.ts`) only
verifies `AdminAuthService.isAuthenticated()` — a binary "is there an active
Telegram admin session." No admin component conditionally shows/hides actions
by the current user's role (grep of `src/app/features/admin`: the `permission`/
`role` matches are all about **managing other users as data** — role labels,
the users-management `AdminRole` record, audit entries — never gating the
current operator's own UI). So today, any authenticated admin can invoke every
admin action the UI exposes; enforcement must be entirely server-side.
The **intended** matrix comes from the dormant Ed25519 flow's `ROLE_PERMISSIONS`
table (`src/app/core/auth/models/permission.model.ts`, mirrored in
docs: finalize BACKEND.md as a self-contained implementation prompt Goal: this document alone, pasted into a fresh Claude session, should be enough for a backend dev to implement against without getting stuck or inventing conventions the frontend doesn't actually need. Fixed a real bug: ~65 cross-references throughout the document pointed to docs/AUTHENTICATION.md, docs/ERROR_CONTRACT.md, docs/MAINTENANCE_MODE.md - three sibling docs that were deleted and fully merged into this document's own §4/§6/§10 during the earlier doc-consolidation pass, but the in-text references were never updated. A fresh agent following those links would hit dead ends repeatedly. Bulk-replaced with in- document section references; hand-fixed ~4 sentences that framed §6/ §10 as "sibling task, in progress" (stale - both are complete, this document's §6/§10 already are the settled contract, nothing to wait on). Added "Recommended default" to every item across the document's 5 consolidated "Requires backend decision" registers (§2.12 framework, §3.21 CRUD cross-cutting - 18 items, §4 §12 auth open items - 9 items, §6 error-model summary - 9 items, §10 maintenance-mode list - 5 items). Each default is derived from what the frontend already implies or standard REST/security convention - no invented APIs or business rules. ~6 items are explicitly flagged as real business/security decisions instead (order state-machine rules, Ed25519 cutover strategy, refresh-token reuse-detection posture) since those carry consequences no amount of frontend-code-reading can resolve. Added a "How to use this document" preamble up front: work in §9's dependency order not document order, apply recommended defaults and keep moving, only stop for the explicitly-flagged business/security items, don't invent beyond what's written or directly implied. Frontend side: confirmed nothing else is left. docs/TODO.md already has zero blockers; docs/KNOWN-ISSUES.md's one open item (unreachable Ed25519 error-code UI) is correctly left open and documented rather than faked closed - fixing it needs a real backend emitting real distinguishable error codes, which doesn't exist yet and can't be fabricated without inventing an API contract. No frontend code touched. No architecture changed. No APIs invented.
2026-07-26 16:10:33 +04:00
`§4 (Authentication, this document)` §9.1). Roles are the string union
`Owner | Administrator | Editor | Support | ReadOnly`; permission domains are
`backoffice`, `builder`, `users`, `settings`:
| Role | backoffice.read | backoffice.write | builder.read | builder.write | users.manage | settings.manage |
|---|:---:|:---:|:---:|:---:|:---:|:---:|
| **Owner** | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| **Administrator** | ✅ | ✅ | ✅ | ✅ | ✅ | — |
| **Editor** | ✅ | ✅ | ✅ | ✅ | — | — |
| **Support** | ✅ | — | — | — | — | — |
| **ReadOnly** | ✅ | — | ✅ | — | — | — |
Guards that would eventually enforce this (`ed25519AuthGuard`,
`permissionGuard(permission)`) exist but are **not referenced by any route**
docs: finalize BACKEND.md as a self-contained implementation prompt Goal: this document alone, pasted into a fresh Claude session, should be enough for a backend dev to implement against without getting stuck or inventing conventions the frontend doesn't actually need. Fixed a real bug: ~65 cross-references throughout the document pointed to docs/AUTHENTICATION.md, docs/ERROR_CONTRACT.md, docs/MAINTENANCE_MODE.md - three sibling docs that were deleted and fully merged into this document's own §4/§6/§10 during the earlier doc-consolidation pass, but the in-text references were never updated. A fresh agent following those links would hit dead ends repeatedly. Bulk-replaced with in- document section references; hand-fixed ~4 sentences that framed §6/ §10 as "sibling task, in progress" (stale - both are complete, this document's §6/§10 already are the settled contract, nothing to wait on). Added "Recommended default" to every item across the document's 5 consolidated "Requires backend decision" registers (§2.12 framework, §3.21 CRUD cross-cutting - 18 items, §4 §12 auth open items - 9 items, §6 error-model summary - 9 items, §10 maintenance-mode list - 5 items). Each default is derived from what the frontend already implies or standard REST/security convention - no invented APIs or business rules. ~6 items are explicitly flagged as real business/security decisions instead (order state-machine rules, Ed25519 cutover strategy, refresh-token reuse-detection posture) since those carry consequences no amount of frontend-code-reading can resolve. Added a "How to use this document" preamble up front: work in §9's dependency order not document order, apply recommended defaults and keep moving, only stop for the explicitly-flagged business/security items, don't invent beyond what's written or directly implied. Frontend side: confirmed nothing else is left. docs/TODO.md already has zero blockers; docs/KNOWN-ISSUES.md's one open item (unreachable Ed25519 error-code UI) is correctly left open and documented rather than faked closed - fixing it needs a real backend emitting real distinguishable error codes, which doesn't exist yet and can't be fabricated without inventing an API contract. No frontend code touched. No architecture changed. No APIs invented.
2026-07-26 16:10:33 +04:00
today (`§4 (Authentication, this document)` §11). The mapping is deliberately coarse —
there is no per-domain granularity (e.g. "edit prices but not delete products")
anywhere client-side.
**Backend requirements / decisions:**
- The backend **must** independently authorize every admin mutation against the
authenticated role — a passing client guard is never proof of authorization.
- **Requires backend decision:** finer-grained per-domain permissions if ever
needed (none implied client-side).
- **Requires backend decision:** the `AdminRole` naming collision (auth string
union vs the users-management `AdminRole` interface with `{id, name,
docs: finalize BACKEND.md as a self-contained implementation prompt Goal: this document alone, pasted into a fresh Claude session, should be enough for a backend dev to implement against without getting stuck or inventing conventions the frontend doesn't actually need. Fixed a real bug: ~65 cross-references throughout the document pointed to docs/AUTHENTICATION.md, docs/ERROR_CONTRACT.md, docs/MAINTENANCE_MODE.md - three sibling docs that were deleted and fully merged into this document's own §4/§6/§10 during the earlier doc-consolidation pass, but the in-text references were never updated. A fresh agent following those links would hit dead ends repeatedly. Bulk-replaced with in- document section references; hand-fixed ~4 sentences that framed §6/ §10 as "sibling task, in progress" (stale - both are complete, this document's §6/§10 already are the settled contract, nothing to wait on). Added "Recommended default" to every item across the document's 5 consolidated "Requires backend decision" registers (§2.12 framework, §3.21 CRUD cross-cutting - 18 items, §4 §12 auth open items - 9 items, §6 error-model summary - 9 items, §10 maintenance-mode list - 5 items). Each default is derived from what the frontend already implies or standard REST/security convention - no invented APIs or business rules. ~6 items are explicitly flagged as real business/security decisions instead (order state-machine rules, Ed25519 cutover strategy, refresh-token reuse-detection posture) since those carry consequences no amount of frontend-code-reading can resolve. Added a "How to use this document" preamble up front: work in §9's dependency order not document order, apply recommended defaults and keep moving, only stop for the explicitly-flagged business/security items, don't invent beyond what's written or directly implied. Frontend side: confirmed nothing else is left. docs/TODO.md already has zero blockers; docs/KNOWN-ISSUES.md's one open item (unreachable Ed25519 error-code UI) is correctly left open and documented rather than faked closed - fixing it needs a real backend emitting real distinguishable error codes, which doesn't exist yet and can't be fabricated without inventing an API contract. No frontend code touched. No architecture changed. No APIs invented.
2026-07-26 16:10:33 +04:00
permissions[], builtIn}`) — flagged in `§4 (Authentication, this document)` §9; the
users-management interface suggests custom/non-builtin roles with arbitrary
permission-string sets, which the coarse 5-role union does not model. Reconcile
before building the backend role table.
---
---
## 6. Error Model
Single unified error-response format the backend must return for every non-2xx
response across all API surfaces (marketplace API, payment/QR API, session
auth API, Ed25519 admin auth API, and any future admin/builder/backoffice
APIs). Derived by cross-referencing every place the Angular frontend
currently parses, catches, or reacts to an HTTP error — see
`docs/context/BACKEND-AUDIT.md` for the full backend-surface audit this is
based on.
**Finding: the frontend does not currently parse any backend error envelope.**
No `HttpInterceptor` in the pipeline (`src/app/app.config.ts`
`mockDataInterceptor, apiBaseUrlInterceptor, apiHeadersInterceptor,
adminAuthHeadersInterceptor, cacheInterceptor`) inspects error responses —
all five only touch outgoing requests or successful GET caching. Every
consumer that reacts to failure does so on the RxJS/`HttpErrorResponse`
level (`error.status`, `error.message`), never on a parsed JSON error body.
The one exception is the Ed25519 admin-auth flow, which has a client-side
`AuthErrorCode` union but (see §"Known frontend gap" below) currently derives
it from **HTTP status only**, not from any body field. Because of this, the
envelope below is a **clean proposal, not a reverse-engineered contract**
every shape decision is marked accordingly.
### The envelope
```json
{
"error": {
"code": "VALIDATION_FAILED",
"message": "One or more fields are invalid.",
"status": 422,
"requestId": "b3f1c2a0-4e21-4d3a-9e77-1e8f6a2d9c11",
"details": [
{ "field": "sku", "code": "REQUIRED", "message": "SKU is required." }
]
}
}
```
**Requires backend decision: adopt this envelope.** The frontend has no
existing opinion to preserve (no code reads `error.error.code` today), so
this is a recommendation, chosen to be consistent with the shapes the
frontend *does* already have opinions about:
- Top-level `{ code, message, status }` mirrors the existing `AuthError`
interface (`src/app/core/auth/models/auth-error.model.ts:13-18`) almost
field-for-field — reusing that shape means the Ed25519 auth module can
parse the new envelope with only a `status` fallback removed, not a
rewrite.
- `details[]` entries `{ field, code, message }` mirror the existing
client-side `ProjectValidationIssue` convention (`code, message, section,
fieldKey, severity` — `src/app/features/project-editor/services/
project-validator.service.ts:23-36`, consumed via `ProjectEditorFacade
.fieldError(fieldKey)`). No backend field-error shape exists to preserve
today (admin CRUD is 100% local/mock — see BACKEND-AUDIT.md §14), so this
is the closest existing frontend convention to align a real one to.
- `requestId` is new (no frontend code reads it yet) — recommended so
support/ops can correlate a user-visible failure to server logs. If
adopted, the frontend would need a small addition to surface it in
error-state UI (not present today).
Field notes:
| Field | Required | Notes |
|---|---|---|
| `error.code` | yes | Stable, machine-readable, `UPPER_SNAKE_CASE`. Never localized. This is what the frontend should branch on, not `message`. |
| `error.message` | yes | Human-readable fallback (English), safe to show only when the frontend has no i18n mapping for `code`. Never the sole signal for UI branching. |
| `error.status` | yes | Must equal the HTTP status of the response (redundant with the transport layer, but the frontend's own `AuthError.status` already carries this, so keep parity). |
| `error.requestId` | recommended | Opaque correlation id, echoed in logs. |
| `error.details` | only for 422 | Array of field-level issues, see §422 below. |
---
### Status-by-status contract
#### 401 — Unauthenticated / expired token
```json
{
"error": {
"code": "UNAUTHENTICATED",
"message": "Authentication is required to access this resource.",
"status": 401,
"requestId": "…"
}
}
```
**Frontend reaction today:**
- **Admin Ed25519 flow** (`AuthService.login()`/`refresh()` in
`src/app/core/auth/services/auth.service.ts`): any `HttpErrorResponse` with
status 401 is mapped via `authErrorCodeFromStatus()` → `AuthErrorCode
'unauthorized'`, surfaced by `AuthErrorPageComponent`
(`src/app/core/auth/pages/auth-error-page.component.ts`) with copy
"Unauthorized… Sign in" and a button that calls `router.navigateByUrl
('/admin-login')`.
- **Customer Telegram session auth** (`TelegramSessionApiService`,
`AuthService` customer-facing, `src/app/services/auth.service.ts`): no
code branches on a 401 status anywhere — session validity is instead
polled via `checkSessionOnce()` returning `AuthSession | null`. **Requires
backend decision**: whether/how a mid-session 401 on a customer-facing
marketplace call (e.g. `POST /cart`, `POST /orders`) should be surfaced —
today it would fall through to each caller's generic `catchError`/`error:`
handler (if any) with no unified "session expired, please re-auth" UX.
- **Admin backoffice CRUD (products/orders/users/etc.)**: these facades
(`AdminUsersFacade`, `AdminOrdersFacade`, …) currently only ever talk to
local/mock gateways, so no real 401 has ever reached them. Their existing
generic `error` boolean signal + `common.errorTitle`/`common.errorDescription`
+ retry button (see "Generic list-page error UI" below) is the pattern a
real 401 would fall into **unless** the facades are updated to branch on
status — they don't today.
#### 403 — Forbidden (wrong role or tenant)
```json
{
"error": {
"code": "FORBIDDEN",
"message": "Your account does not have permission to perform this action.",
"status": 403,
"requestId": "…"
}
}
```
**Frontend reaction today:** Ed25519 admin flow only. `authErrorCodeFromStatus(403)`
`'forbidden'``AuthErrorPageComponent` copy "Forbidden… Back to
dashboard", button `router.navigateByUrl('/backoffice')`. No tenant-scoping
distinction exists in this code path — a 403 caused by wrong role and a 403
caused by wrong tenant render identical copy today. **Requires backend
decision**: if tenant-mismatch should be visually distinct from
role-mismatch, it needs its own `error.code` (e.g. `TENANT_FORBIDDEN` vs
`ROLE_FORBIDDEN`) since the frontend has no other signal to key off besides
status today.
#### 404 — Not found
```json
{
"error": {
"code": "NOT_FOUND",
"message": "The requested item could not be found.",
"status": 404,
"requestId": "…"
}
}
```
**Frontend reaction today:** No code path distinguishes 404 from any other
failure. `catalog-container.component.ts` and
`product-details-container.component.ts` both catch *any* load error and
render the same generic `catalog.errorTitle`/`productDetails.errorTitle`
empty-state (`en.ts:176,277`) — a real 404 (product deleted) and a 500
(server crash) look identical to the user today. **Requires backend
decision**: whether the frontend should be enhanced to show a distinct
"this product no longer exists" message for 404 specifically (would need a
status/code check added to those two containers — not present now).
#### 409 — Conflict
```json
{
"error": {
"code": "CONFLICT",
"message": "A category with this slug already exists.",
"status": 409,
"requestId": "…"
}
}
```
**Frontend reaction today:** no code catches or branches on 409 anywhere.
The one related concept in the codebase is `AdminCategoriesGateway
.isSlugTaken(slug, excludingId)` (BACKEND-AUDIT.md §14) — a **proactive**
pre-check call the frontend makes *before* submitting, not a reaction to a
409 conflict response. **Requires backend decision**: whether create/update
endpoints should also return 409 on the same slug/uniqueness conflict as a
race-condition backstop, and whether the frontend should add a 409 handler
that surfaces `error.details` inline (there is no such handler today —
`isSlugTaken` is the only existing conflict-avoidance mechanism, and it is
best-effort/TOCTOU-prone).
#### 422 — Validation failure
```json
{
"error": {
"code": "VALIDATION_FAILED",
"message": "One or more fields are invalid.",
"status": 422,
"requestId": "…",
"details": [
{ "field": "sku", "code": "REQUIRED", "message": "SKU is required." },
{ "field": "price", "code": "OUT_OF_RANGE", "message": "Price must be greater than 0." }
]
}
}
```
**Frontend reaction today:** no admin form currently parses a backend
validation-error body — all admin CRUD is local/mock (BACKEND-AUDIT.md §14),
so there has never been a real 422 to react to. The frontend **does** have
an established field-error UI convention worth preserving: `ProjectEditorFacade
.fieldError(fieldKey): string | null`
(`src/app/features/project-editor/facade/project-editor.facade.ts:371-374`)
reads from `issuesByField` (a `Map<fieldKey, ProjectValidationIssue[]>`) and
returns the first issue's `message`, for inline per-field template binding.
That mechanism is entirely client-side validation today (`ProjectValidator`
service), not backend-driven. **Requires backend decision**: adopting
`details[].field` as the join key would let a future `fieldError()`-style
adapter merge backend 422 errors into the same inline-error UI pattern
without inventing a second one — but the adapter itself does not exist yet
and would need to be built.
#### 429 — Rate limited
```json
{
"error": {
"code": "RATE_LIMITED",
"message": "Too many requests. Please slow down.",
"status": 429,
"requestId": "…",
"retryAfterSeconds": 30
}
}
```
**Frontend reaction today: none whatsoever.** No interceptor, facade, or
component in the codebase references `429` or "rate limit" in any form (grepped
across `src/`). **Requires backend decision** on every aspect:
- Whether the backend sends a `Retry-After` HTTP header, a body field
(`retryAfterSeconds` above), or both.
- Whether the frontend should retry automatically (with backoff) or only
show the user a "please wait Ns" message. Recommend: since no retry
interceptor exists today, add one is a new build item, not a config
change.
#### 500 — Server error
```json
{
"error": {
"code": "INTERNAL_ERROR",
"message": "An unexpected error occurred. Please try again.",
"status": 500,
"requestId": "…"
}
}
```
**Frontend reaction today:** falls into whichever generic catch-all a given
caller has:
- Ed25519 admin flow: `authErrorCodeFromStatus()` default branch → `status
>= 500 ? 'backend-unavailable' : 'unauthorized'` → same
"Backend unavailable… Retry" screen as a network-down 503 (see below) —
the frontend does not distinguish "server is up but this request 500'd"
from "server is completely unreachable."
- Admin list pages (`AdminUsersFacade` and siblings): generic `error`
boolean signal set to `true` in the RxJS `error:` callback, rendering
`common.errorTitle`/`common.errorDescription` + a retry button that
re-invokes the same load call. No status differentiation.
- Storefront catalog/product pages: same generic empty-state pattern as 404
above.
- `LocationService.getRegions()`-equivalent: falls back silently to 6
hardcoded regions on *any* error (including 500), no user-visible error at
all (`src/app/services/location.service.ts`).
#### 503 — Maintenance / unavailable
```json
{
"error": {
"code": "SERVICE_UNAVAILABLE",
"message": "The service is temporarily unavailable. Please try again shortly.",
"status": 503,
"requestId": "…"
}
}
```
**Frontend reaction today:** Ed25519 admin flow only, via the same
`status >= 500` branch as 500 above → `'backend-unavailable'`
`AuthErrorPageComponent` "Backend unavailable… Retry." No other code path
reacts to 503 specifically today (marketplace API calls that 503 would just
fall into each caller's generic error handling, same as 500 above).
**Distinguishing signal from Maintenance mode (see next section):** use
`error.code`, not the HTTP status. A plain infra 503 (database down,
overload) should send `"code": "SERVICE_UNAVAILABLE"`; a deliberate
maintenance window should send `"code": "MAINTENANCE_MODE"` (still with HTTP
status 503, since it's a byte-identical "the service is not accepting
requests" situation, but a different reason). This is the only way for the
frontend to build a distinct maintenance-mode UX later, since status alone
docs: finalize BACKEND.md as a self-contained implementation prompt Goal: this document alone, pasted into a fresh Claude session, should be enough for a backend dev to implement against without getting stuck or inventing conventions the frontend doesn't actually need. Fixed a real bug: ~65 cross-references throughout the document pointed to docs/AUTHENTICATION.md, docs/ERROR_CONTRACT.md, docs/MAINTENANCE_MODE.md - three sibling docs that were deleted and fully merged into this document's own §4/§6/§10 during the earlier doc-consolidation pass, but the in-text references were never updated. A fresh agent following those links would hit dead ends repeatedly. Bulk-replaced with in- document section references; hand-fixed ~4 sentences that framed §6/ §10 as "sibling task, in progress" (stale - both are complete, this document's §6/§10 already are the settled contract, nothing to wait on). Added "Recommended default" to every item across the document's 5 consolidated "Requires backend decision" registers (§2.12 framework, §3.21 CRUD cross-cutting - 18 items, §4 §12 auth open items - 9 items, §6 error-model summary - 9 items, §10 maintenance-mode list - 5 items). Each default is derived from what the frontend already implies or standard REST/security convention - no invented APIs or business rules. ~6 items are explicitly flagged as real business/security decisions instead (order state-machine rules, Ed25519 cutover strategy, refresh-token reuse-detection posture) since those carry consequences no amount of frontend-code-reading can resolve. Added a "How to use this document" preamble up front: work in §9's dependency order not document order, apply recommended defaults and keep moving, only stop for the explicitly-flagged business/security items, don't invent beyond what's written or directly implied. Frontend side: confirmed nothing else is left. docs/TODO.md already has zero blockers; docs/KNOWN-ISSUES.md's one open item (unreachable Ed25519 error-code UI) is correctly left open and documented rather than faked closed - fixing it needs a real backend emitting real distinguishable error codes, which doesn't exist yet and can't be fabricated without inventing an API contract. No frontend code touched. No architecture changed. No APIs invented.
2026-07-26 16:10:33 +04:00
is not enough. §10 (Maintenance Mode) owns the UX/copy for the maintenance
case — this section only fixes the wire signal it must key off
(`error.code === "MAINTENANCE_MODE"`), consistent with §10 without
duplicating UX detail here.
#### Maintenance mode
Same HTTP status as above (503), distinguished purely by `error.code`:
```json
{
"error": {
"code": "MAINTENANCE_MODE",
"message": "This marketplace is temporarily down for maintenance.",
"status": 503,
"requestId": "…",
"maintenanceUntil": "2026-07-26T04:00:00Z"
}
}
```
`maintenanceUntil` (ISO 8601, optional) lets the maintenance-mode UX (sibling
doc) show an ETA if the backend has one. **Requires backend decision:**
whether `maintenanceUntil` is populated reliably enough to promise in UI, or
should be treated as advisory-only.
**Frontend reaction today:** none — no maintenance-mode concept exists in
the frontend at all currently (confirmed: no matches for "maintenance" in
docs: finalize BACKEND.md as a self-contained implementation prompt Goal: this document alone, pasted into a fresh Claude session, should be enough for a backend dev to implement against without getting stuck or inventing conventions the frontend doesn't actually need. Fixed a real bug: ~65 cross-references throughout the document pointed to docs/AUTHENTICATION.md, docs/ERROR_CONTRACT.md, docs/MAINTENANCE_MODE.md - three sibling docs that were deleted and fully merged into this document's own §4/§6/§10 during the earlier doc-consolidation pass, but the in-text references were never updated. A fresh agent following those links would hit dead ends repeatedly. Bulk-replaced with in- document section references; hand-fixed ~4 sentences that framed §6/ §10 as "sibling task, in progress" (stale - both are complete, this document's §6/§10 already are the settled contract, nothing to wait on). Added "Recommended default" to every item across the document's 5 consolidated "Requires backend decision" registers (§2.12 framework, §3.21 CRUD cross-cutting - 18 items, §4 §12 auth open items - 9 items, §6 error-model summary - 9 items, §10 maintenance-mode list - 5 items). Each default is derived from what the frontend already implies or standard REST/security convention - no invented APIs or business rules. ~6 items are explicitly flagged as real business/security decisions instead (order state-machine rules, Ed25519 cutover strategy, refresh-token reuse-detection posture) since those carry consequences no amount of frontend-code-reading can resolve. Added a "How to use this document" preamble up front: work in §9's dependency order not document order, apply recommended defaults and keep moving, only stop for the explicitly-flagged business/security items, don't invent beyond what's written or directly implied. Frontend side: confirmed nothing else is left. docs/TODO.md already has zero blockers; docs/KNOWN-ISSUES.md's one open item (unreachable Ed25519 error-code UI) is correctly left open and documented rather than faked closed - fixing it needs a real backend emitting real distinguishable error codes, which doesn't exist yet and can't be fabricated without inventing an API contract. No frontend code touched. No architecture changed. No APIs invented.
2026-07-26 16:10:33 +04:00
`src/`). This entire row is new; the sibling `§10 (Maintenance Mode, this document)` task
should treat it as building from scratch, not preserving anything.
#### Tenant disabled
```json
{
"error": {
"code": "TENANT_DISABLED",
"message": "This marketplace is not currently active.",
"status": 403,
"requestId": "…"
}
}
```
**Frontend reaction today: none.** Tenant resolution
(`TenantResolverService`, `src/app/core/config/tenant-resolver.service.ts`)
only ever resolves *which* tenant a request targets (via host/subdomain); no
code path in the audited surface handles a backend telling the frontend
"this tenant exists but is disabled." **Requires backend decision** end to
end: status code (403 recommended, to reuse the existing `forbidden`
auth-error screen plumbing, vs. a dedicated status), and whether this should
route to a dedicated "tenant disabled" screen or reuse
`AuthErrorPageComponent`'s `forbidden` copy (which currently says "Your
account role does not have permission" — wrong wording for a
tenant-disabled scenario, would need a new `AuthErrorCode` entry and copy if
reused).
#### Rate limit
See **429** above — same contract, called out separately here only because
the task list asked for it as its own row. No additional distinguishing
signal needed beyond the 429 status + `RATE_LIMITED` code.
#### Expired token
```json
{
"error": {
"code": "TOKEN_EXPIRED",
"message": "Your session has expired. Please sign in again.",
"status": 401,
"requestId": "…"
}
}
```
**Frontend reaction today — known gap, read carefully:** `AuthService
.refresh()` (`src/app/core/auth/services/auth.service.ts:65-76`) has a
client-side `AuthErrorCode` value `'session-expired'` and passes it as
`fallbackCode` into `handleAuthError()`. **However**, `toAuthErrorShape()`
(lines 110-118) only uses `fallbackCode` when the caught error is **not** an
`HttpErrorResponse` — for an actual HTTP error it always calls
`authErrorCodeFromStatus(error.status)`, which maps 401 → `'unauthorized'`,
never `'session-expired'`, regardless of `fallbackCode`. So today, a real
backend 401 on `/refresh` renders the **generic "Unauthorized" screen**, not
"Session expired" — the "Session expired" screen is only ever reached via
the *no-refresh-token-present* client-side branch (line 67-70), never from a
real HTTP response. **Requires backend decision + frontend fix**: for a
distinct "your session expired, please sign in again" screen to actually
render on a real backend 401, either (a) the backend returns a body
`error.code: "TOKEN_EXPIRED"` and the frontend is updated to read it instead
of relying solely on `authErrorCodeFromStatus(status)`, or (b) this
distinction is accepted as unreachable today and left as future work. Flag
this gap explicitly to whoever picks up the fix — it is a pre-existing bug,
not something this contract can silently paper over.
#### Invalid signature
```json
{
"error": {
"code": "INVALID_SIGNATURE",
"message": "The signed challenge could not be verified.",
"status": 401,
"requestId": "…"
}
}
```
docs: finalize BACKEND.md as a self-contained implementation prompt Goal: this document alone, pasted into a fresh Claude session, should be enough for a backend dev to implement against without getting stuck or inventing conventions the frontend doesn't actually need. Fixed a real bug: ~65 cross-references throughout the document pointed to docs/AUTHENTICATION.md, docs/ERROR_CONTRACT.md, docs/MAINTENANCE_MODE.md - three sibling docs that were deleted and fully merged into this document's own §4/§6/§10 during the earlier doc-consolidation pass, but the in-text references were never updated. A fresh agent following those links would hit dead ends repeatedly. Bulk-replaced with in- document section references; hand-fixed ~4 sentences that framed §6/ §10 as "sibling task, in progress" (stale - both are complete, this document's §6/§10 already are the settled contract, nothing to wait on). Added "Recommended default" to every item across the document's 5 consolidated "Requires backend decision" registers (§2.12 framework, §3.21 CRUD cross-cutting - 18 items, §4 §12 auth open items - 9 items, §6 error-model summary - 9 items, §10 maintenance-mode list - 5 items). Each default is derived from what the frontend already implies or standard REST/security convention - no invented APIs or business rules. ~6 items are explicitly flagged as real business/security decisions instead (order state-machine rules, Ed25519 cutover strategy, refresh-token reuse-detection posture) since those carry consequences no amount of frontend-code-reading can resolve. Added a "How to use this document" preamble up front: work in §9's dependency order not document order, apply recommended defaults and keep moving, only stop for the explicitly-flagged business/security items, don't invent beyond what's written or directly implied. Frontend side: confirmed nothing else is left. docs/TODO.md already has zero blockers; docs/KNOWN-ISSUES.md's one open item (unreachable Ed25519 error-code UI) is correctly left open and documented rather than faked closed - fixing it needs a real backend emitting real distinguishable error codes, which doesn't exist yet and can't be fabricated without inventing an API contract. No frontend code touched. No architecture changed. No APIs invented.
2026-07-26 16:10:33 +04:00
Ties to the Ed25519 admin-auth flow documented in §4 — keep the `code`
value (`INVALID_SIGNATURE`) consistent with what §4 names the failure mode;
this contract only defines the wire shape, §4 owns the auth-flow narrative.
**Frontend reaction today:** same gap as "Expired token" above.
`AuthService.login()` passes `fallbackCode: 'invalid-signature'` into
`handleAuthError()`, but `toAuthErrorShape()` discards it for any real
`HttpErrorResponse` and maps a 401 from `/verify` to the generic
`'unauthorized'` screen via `authErrorCodeFromStatus()`. The dedicated
"Invalid signature… Try again" screen
(`src/app/core/auth/pages/auth-error-page.component.ts:21-25`) exists in the
copy table but is **currently unreachable from a real backend response** for
the same reason as `session-expired` above. **Requires backend decision +
frontend fix**: backend must send a body-level `error.code:
"INVALID_SIGNATURE"` and the frontend's `toAuthErrorShape()` must be updated
to prefer a body code over the status-only mapping, or this screen stays
dead code reachable only via non-HTTP error paths.
---
### Generic list-page error UI (for reference)
Every admin backoffice list page (`AdminUsersFacade`, `AdminOrdersFacade`,
`AdminMonitoringFacade`, `AdminModerationFacade`, `AdminTransactionsFacade`,
`AdminProductsFacade`, `AdminCategoriesFacade`, `AdminAnalyticsFacade`,
`AdminCustomersFacade`, `AdminDashboardFacade`) follows the same shape,
added by RC-02 (`e153a67 fix(backoffice): add error+retry states to Users,
Monitoring, Analytics, Reports`):
```ts
readonly error = signal(false);
// on load:
error: () => { this.items.set([]); this.loading.set(false); this.error.set(true); }
```
```html
@else if (facade.error()) {
<app-empty-state [title]="'common.errorTitle' | translate" [description]="'common.errorDescription' | translate">
<span slot="actions">
<app-button variant="primary" (click)="facade.loadAll()">{{ 'common.retry' | translate }}</app-button>
</span>
</app-empty-state>
}
```
This is a **boolean** error flag — it does not branch on HTTP status or
`error.code` today. Every status in this contract (401/403/404/409/422/429/
500/503) would currently collapse into the same generic "Something went
wrong / retry" UI on these pages **unless** the facades are individually
updated to inspect `error.code`/`status` and branch — none do today. Wiring
that up is out of scope for this document (it defines the wire contract);
flagging it here so whoever wires real backends into these facades knows
the current ceiling of frontend error UX is "generic retry," not
per-status handling, except in the Ed25519 admin-auth module.
---
### Summary: "Requires backend decision" items
docs: finalize BACKEND.md as a self-contained implementation prompt Goal: this document alone, pasted into a fresh Claude session, should be enough for a backend dev to implement against without getting stuck or inventing conventions the frontend doesn't actually need. Fixed a real bug: ~65 cross-references throughout the document pointed to docs/AUTHENTICATION.md, docs/ERROR_CONTRACT.md, docs/MAINTENANCE_MODE.md - three sibling docs that were deleted and fully merged into this document's own §4/§6/§10 during the earlier doc-consolidation pass, but the in-text references were never updated. A fresh agent following those links would hit dead ends repeatedly. Bulk-replaced with in- document section references; hand-fixed ~4 sentences that framed §6/ §10 as "sibling task, in progress" (stale - both are complete, this document's §6/§10 already are the settled contract, nothing to wait on). Added "Recommended default" to every item across the document's 5 consolidated "Requires backend decision" registers (§2.12 framework, §3.21 CRUD cross-cutting - 18 items, §4 §12 auth open items - 9 items, §6 error-model summary - 9 items, §10 maintenance-mode list - 5 items). Each default is derived from what the frontend already implies or standard REST/security convention - no invented APIs or business rules. ~6 items are explicitly flagged as real business/security decisions instead (order state-machine rules, Ed25519 cutover strategy, refresh-token reuse-detection posture) since those carry consequences no amount of frontend-code-reading can resolve. Added a "How to use this document" preamble up front: work in §9's dependency order not document order, apply recommended defaults and keep moving, only stop for the explicitly-flagged business/security items, don't invent beyond what's written or directly implied. Frontend side: confirmed nothing else is left. docs/TODO.md already has zero blockers; docs/KNOWN-ISSUES.md's one open item (unreachable Ed25519 error-code UI) is correctly left open and documented rather than faked closed - fixing it needs a real backend emitting real distinguishable error codes, which doesn't exist yet and can't be fabricated without inventing an API contract. No frontend code touched. No architecture changed. No APIs invented.
2026-07-26 16:10:33 +04:00
Each has a **Recommended default** — apply it unless flagged otherwise.
- **Envelope adoption** — the `{ error: { code, message, status, requestId,
details? } }` shape itself; no frontend code parses any envelope today.
docs: finalize BACKEND.md as a self-contained implementation prompt Goal: this document alone, pasted into a fresh Claude session, should be enough for a backend dev to implement against without getting stuck or inventing conventions the frontend doesn't actually need. Fixed a real bug: ~65 cross-references throughout the document pointed to docs/AUTHENTICATION.md, docs/ERROR_CONTRACT.md, docs/MAINTENANCE_MODE.md - three sibling docs that were deleted and fully merged into this document's own §4/§6/§10 during the earlier doc-consolidation pass, but the in-text references were never updated. A fresh agent following those links would hit dead ends repeatedly. Bulk-replaced with in- document section references; hand-fixed ~4 sentences that framed §6/ §10 as "sibling task, in progress" (stale - both are complete, this document's §6/§10 already are the settled contract, nothing to wait on). Added "Recommended default" to every item across the document's 5 consolidated "Requires backend decision" registers (§2.12 framework, §3.21 CRUD cross-cutting - 18 items, §4 §12 auth open items - 9 items, §6 error-model summary - 9 items, §10 maintenance-mode list - 5 items). Each default is derived from what the frontend already implies or standard REST/security convention - no invented APIs or business rules. ~6 items are explicitly flagged as real business/security decisions instead (order state-machine rules, Ed25519 cutover strategy, refresh-token reuse-detection posture) since those carry consequences no amount of frontend-code-reading can resolve. Added a "How to use this document" preamble up front: work in §9's dependency order not document order, apply recommended defaults and keep moving, only stop for the explicitly-flagged business/security items, don't invent beyond what's written or directly implied. Frontend side: confirmed nothing else is left. docs/TODO.md already has zero blockers; docs/KNOWN-ISSUES.md's one open item (unreachable Ed25519 error-code UI) is correctly left open and documented rather than faked closed - fixing it needs a real backend emitting real distinguishable error codes, which doesn't exist yet and can't be fabricated without inventing an API contract. No frontend code touched. No architecture changed. No APIs invented.
2026-07-26 16:10:33 +04:00
**Recommended default:** adopt it as specified — every status-by-status
example in this section already uses it; it's the shape to build against.
- **401 on customer-facing marketplace calls** (`/cart`, `/orders`, etc.) —
no unified "session expired, please re-auth" UX exists for the customer
Telegram-session flow.
docs: finalize BACKEND.md as a self-contained implementation prompt Goal: this document alone, pasted into a fresh Claude session, should be enough for a backend dev to implement against without getting stuck or inventing conventions the frontend doesn't actually need. Fixed a real bug: ~65 cross-references throughout the document pointed to docs/AUTHENTICATION.md, docs/ERROR_CONTRACT.md, docs/MAINTENANCE_MODE.md - three sibling docs that were deleted and fully merged into this document's own §4/§6/§10 during the earlier doc-consolidation pass, but the in-text references were never updated. A fresh agent following those links would hit dead ends repeatedly. Bulk-replaced with in- document section references; hand-fixed ~4 sentences that framed §6/ §10 as "sibling task, in progress" (stale - both are complete, this document's §6/§10 already are the settled contract, nothing to wait on). Added "Recommended default" to every item across the document's 5 consolidated "Requires backend decision" registers (§2.12 framework, §3.21 CRUD cross-cutting - 18 items, §4 §12 auth open items - 9 items, §6 error-model summary - 9 items, §10 maintenance-mode list - 5 items). Each default is derived from what the frontend already implies or standard REST/security convention - no invented APIs or business rules. ~6 items are explicitly flagged as real business/security decisions instead (order state-machine rules, Ed25519 cutover strategy, refresh-token reuse-detection posture) since those carry consequences no amount of frontend-code-reading can resolve. Added a "How to use this document" preamble up front: work in §9's dependency order not document order, apply recommended defaults and keep moving, only stop for the explicitly-flagged business/security items, don't invent beyond what's written or directly implied. Frontend side: confirmed nothing else is left. docs/TODO.md already has zero blockers; docs/KNOWN-ISSUES.md's one open item (unreachable Ed25519 error-code UI) is correctly left open and documented rather than faked closed - fixing it needs a real backend emitting real distinguishable error codes, which doesn't exist yet and can't be fabricated without inventing an API contract. No frontend code touched. No architecture changed. No APIs invented.
2026-07-26 16:10:33 +04:00
**Recommended default:** return the standard envelope with
`code: "UNAUTHORIZED"`; the frontend re-auth UX is a follow-up frontend
task once the backend consistently returns this, not something to block
the backend on.
- **403 tenant-mismatch vs role-mismatch** distinct copy/code.
docs: finalize BACKEND.md as a self-contained implementation prompt Goal: this document alone, pasted into a fresh Claude session, should be enough for a backend dev to implement against without getting stuck or inventing conventions the frontend doesn't actually need. Fixed a real bug: ~65 cross-references throughout the document pointed to docs/AUTHENTICATION.md, docs/ERROR_CONTRACT.md, docs/MAINTENANCE_MODE.md - three sibling docs that were deleted and fully merged into this document's own §4/§6/§10 during the earlier doc-consolidation pass, but the in-text references were never updated. A fresh agent following those links would hit dead ends repeatedly. Bulk-replaced with in- document section references; hand-fixed ~4 sentences that framed §6/ §10 as "sibling task, in progress" (stale - both are complete, this document's §6/§10 already are the settled contract, nothing to wait on). Added "Recommended default" to every item across the document's 5 consolidated "Requires backend decision" registers (§2.12 framework, §3.21 CRUD cross-cutting - 18 items, §4 §12 auth open items - 9 items, §6 error-model summary - 9 items, §10 maintenance-mode list - 5 items). Each default is derived from what the frontend already implies or standard REST/security convention - no invented APIs or business rules. ~6 items are explicitly flagged as real business/security decisions instead (order state-machine rules, Ed25519 cutover strategy, refresh-token reuse-detection posture) since those carry consequences no amount of frontend-code-reading can resolve. Added a "How to use this document" preamble up front: work in §9's dependency order not document order, apply recommended defaults and keep moving, only stop for the explicitly-flagged business/security items, don't invent beyond what's written or directly implied. Frontend side: confirmed nothing else is left. docs/TODO.md already has zero blockers; docs/KNOWN-ISSUES.md's one open item (unreachable Ed25519 error-code UI) is correctly left open and documented rather than faked closed - fixing it needs a real backend emitting real distinguishable error codes, which doesn't exist yet and can't be fabricated without inventing an API contract. No frontend code touched. No architecture changed. No APIs invented.
2026-07-26 16:10:33 +04:00
**Recommended default:** two distinct codes (`TENANT_MISMATCH`,
`FORBIDDEN`) — the frontend can pick copy per-code once they exist;
collapsing them into one code loses information for no benefit.
- **404 vs generic-error distinct UX** on catalog/product pages (currently
identical).
docs: finalize BACKEND.md as a self-contained implementation prompt Goal: this document alone, pasted into a fresh Claude session, should be enough for a backend dev to implement against without getting stuck or inventing conventions the frontend doesn't actually need. Fixed a real bug: ~65 cross-references throughout the document pointed to docs/AUTHENTICATION.md, docs/ERROR_CONTRACT.md, docs/MAINTENANCE_MODE.md - three sibling docs that were deleted and fully merged into this document's own §4/§6/§10 during the earlier doc-consolidation pass, but the in-text references were never updated. A fresh agent following those links would hit dead ends repeatedly. Bulk-replaced with in- document section references; hand-fixed ~4 sentences that framed §6/ §10 as "sibling task, in progress" (stale - both are complete, this document's §6/§10 already are the settled contract, nothing to wait on). Added "Recommended default" to every item across the document's 5 consolidated "Requires backend decision" registers (§2.12 framework, §3.21 CRUD cross-cutting - 18 items, §4 §12 auth open items - 9 items, §6 error-model summary - 9 items, §10 maintenance-mode list - 5 items). Each default is derived from what the frontend already implies or standard REST/security convention - no invented APIs or business rules. ~6 items are explicitly flagged as real business/security decisions instead (order state-machine rules, Ed25519 cutover strategy, refresh-token reuse-detection posture) since those carry consequences no amount of frontend-code-reading can resolve. Added a "How to use this document" preamble up front: work in §9's dependency order not document order, apply recommended defaults and keep moving, only stop for the explicitly-flagged business/security items, don't invent beyond what's written or directly implied. Frontend side: confirmed nothing else is left. docs/TODO.md already has zero blockers; docs/KNOWN-ISSUES.md's one open item (unreachable Ed25519 error-code UI) is correctly left open and documented rather than faked closed - fixing it needs a real backend emitting real distinguishable error codes, which doesn't exist yet and can't be fabricated without inventing an API contract. No frontend code touched. No architecture changed. No APIs invented.
2026-07-26 16:10:33 +04:00
**Recommended default:** return real `404` for missing resources (not a
generic `500`) — this is a backend correctness item, not a decision;
the frontend UX unification is a separate, non-blocking follow-up.
- **409 conflict handling on submit** (today only a proactive
`isSlugTaken` pre-check exists; no reactive 409 handler).
docs: finalize BACKEND.md as a self-contained implementation prompt Goal: this document alone, pasted into a fresh Claude session, should be enough for a backend dev to implement against without getting stuck or inventing conventions the frontend doesn't actually need. Fixed a real bug: ~65 cross-references throughout the document pointed to docs/AUTHENTICATION.md, docs/ERROR_CONTRACT.md, docs/MAINTENANCE_MODE.md - three sibling docs that were deleted and fully merged into this document's own §4/§6/§10 during the earlier doc-consolidation pass, but the in-text references were never updated. A fresh agent following those links would hit dead ends repeatedly. Bulk-replaced with in- document section references; hand-fixed ~4 sentences that framed §6/ §10 as "sibling task, in progress" (stale - both are complete, this document's §6/§10 already are the settled contract, nothing to wait on). Added "Recommended default" to every item across the document's 5 consolidated "Requires backend decision" registers (§2.12 framework, §3.21 CRUD cross-cutting - 18 items, §4 §12 auth open items - 9 items, §6 error-model summary - 9 items, §10 maintenance-mode list - 5 items). Each default is derived from what the frontend already implies or standard REST/security convention - no invented APIs or business rules. ~6 items are explicitly flagged as real business/security decisions instead (order state-machine rules, Ed25519 cutover strategy, refresh-token reuse-detection posture) since those carry consequences no amount of frontend-code-reading can resolve. Added a "How to use this document" preamble up front: work in §9's dependency order not document order, apply recommended defaults and keep moving, only stop for the explicitly-flagged business/security items, don't invent beyond what's written or directly implied. Frontend side: confirmed nothing else is left. docs/TODO.md already has zero blockers; docs/KNOWN-ISSUES.md's one open item (unreachable Ed25519 error-code UI) is correctly left open and documented rather than faked closed - fixing it needs a real backend emitting real distinguishable error codes, which doesn't exist yet and can't be fabricated without inventing an API contract. No frontend code touched. No architecture changed. No APIs invented.
2026-07-26 16:10:33 +04:00
**Recommended default:** return `409` with the standard envelope on any
uniqueness/concurrency conflict; treat the proactive pre-check as
best-effort only (§3.21 item 5 — TOCTOU-prone by nature).
- **422 `details[]` → inline field-error adapter** for admin forms (the
client-side `fieldError()` convention exists but nothing feeds it from a
backend response yet).
docs: finalize BACKEND.md as a self-contained implementation prompt Goal: this document alone, pasted into a fresh Claude session, should be enough for a backend dev to implement against without getting stuck or inventing conventions the frontend doesn't actually need. Fixed a real bug: ~65 cross-references throughout the document pointed to docs/AUTHENTICATION.md, docs/ERROR_CONTRACT.md, docs/MAINTENANCE_MODE.md - three sibling docs that were deleted and fully merged into this document's own §4/§6/§10 during the earlier doc-consolidation pass, but the in-text references were never updated. A fresh agent following those links would hit dead ends repeatedly. Bulk-replaced with in- document section references; hand-fixed ~4 sentences that framed §6/ §10 as "sibling task, in progress" (stale - both are complete, this document's §6/§10 already are the settled contract, nothing to wait on). Added "Recommended default" to every item across the document's 5 consolidated "Requires backend decision" registers (§2.12 framework, §3.21 CRUD cross-cutting - 18 items, §4 §12 auth open items - 9 items, §6 error-model summary - 9 items, §10 maintenance-mode list - 5 items). Each default is derived from what the frontend already implies or standard REST/security convention - no invented APIs or business rules. ~6 items are explicitly flagged as real business/security decisions instead (order state-machine rules, Ed25519 cutover strategy, refresh-token reuse-detection posture) since those carry consequences no amount of frontend-code-reading can resolve. Added a "How to use this document" preamble up front: work in §9's dependency order not document order, apply recommended defaults and keep moving, only stop for the explicitly-flagged business/security items, don't invent beyond what's written or directly implied. Frontend side: confirmed nothing else is left. docs/TODO.md already has zero blockers; docs/KNOWN-ISSUES.md's one open item (unreachable Ed25519 error-code UI) is correctly left open and documented rather than faked closed - fixing it needs a real backend emitting real distinguishable error codes, which doesn't exist yet and can't be fabricated without inventing an API contract. No frontend code touched. No architecture changed. No APIs invented.
2026-07-26 16:10:33 +04:00
**Recommended default:** populate `details[]` as `{ field, message }` per
invalid field on every `422` — the client-side adapter already expects
this shape, it just has nothing to consume yet.
- **429 rate-limit contract end to end** — header vs body, retry-after
value, and whether the frontend auto-retries (nothing exists today).
docs: finalize BACKEND.md as a self-contained implementation prompt Goal: this document alone, pasted into a fresh Claude session, should be enough for a backend dev to implement against without getting stuck or inventing conventions the frontend doesn't actually need. Fixed a real bug: ~65 cross-references throughout the document pointed to docs/AUTHENTICATION.md, docs/ERROR_CONTRACT.md, docs/MAINTENANCE_MODE.md - three sibling docs that were deleted and fully merged into this document's own §4/§6/§10 during the earlier doc-consolidation pass, but the in-text references were never updated. A fresh agent following those links would hit dead ends repeatedly. Bulk-replaced with in- document section references; hand-fixed ~4 sentences that framed §6/ §10 as "sibling task, in progress" (stale - both are complete, this document's §6/§10 already are the settled contract, nothing to wait on). Added "Recommended default" to every item across the document's 5 consolidated "Requires backend decision" registers (§2.12 framework, §3.21 CRUD cross-cutting - 18 items, §4 §12 auth open items - 9 items, §6 error-model summary - 9 items, §10 maintenance-mode list - 5 items). Each default is derived from what the frontend already implies or standard REST/security convention - no invented APIs or business rules. ~6 items are explicitly flagged as real business/security decisions instead (order state-machine rules, Ed25519 cutover strategy, refresh-token reuse-detection posture) since those carry consequences no amount of frontend-code-reading can resolve. Added a "How to use this document" preamble up front: work in §9's dependency order not document order, apply recommended defaults and keep moving, only stop for the explicitly-flagged business/security items, don't invent beyond what's written or directly implied. Frontend side: confirmed nothing else is left. docs/TODO.md already has zero blockers; docs/KNOWN-ISSUES.md's one open item (unreachable Ed25519 error-code UI) is correctly left open and documented rather than faked closed - fixing it needs a real backend emitting real distinguishable error codes, which doesn't exist yet and can't be fabricated without inventing an API contract. No frontend code touched. No architecture changed. No APIs invented.
2026-07-26 16:10:33 +04:00
**Recommended default:** standard `Retry-After` header (seconds) +
envelope body with `code: "RATE_LIMITED"`; no frontend auto-retry (the
user retries manually) — simplest safe default, matches how the rest of
the app already treats errors as terminal-until-user-action.
- **Maintenance-mode `maintenanceUntil` reliability** — advisory only, or
can the frontend promise an ETA.
docs: finalize BACKEND.md as a self-contained implementation prompt Goal: this document alone, pasted into a fresh Claude session, should be enough for a backend dev to implement against without getting stuck or inventing conventions the frontend doesn't actually need. Fixed a real bug: ~65 cross-references throughout the document pointed to docs/AUTHENTICATION.md, docs/ERROR_CONTRACT.md, docs/MAINTENANCE_MODE.md - three sibling docs that were deleted and fully merged into this document's own §4/§6/§10 during the earlier doc-consolidation pass, but the in-text references were never updated. A fresh agent following those links would hit dead ends repeatedly. Bulk-replaced with in- document section references; hand-fixed ~4 sentences that framed §6/ §10 as "sibling task, in progress" (stale - both are complete, this document's §6/§10 already are the settled contract, nothing to wait on). Added "Recommended default" to every item across the document's 5 consolidated "Requires backend decision" registers (§2.12 framework, §3.21 CRUD cross-cutting - 18 items, §4 §12 auth open items - 9 items, §6 error-model summary - 9 items, §10 maintenance-mode list - 5 items). Each default is derived from what the frontend already implies or standard REST/security convention - no invented APIs or business rules. ~6 items are explicitly flagged as real business/security decisions instead (order state-machine rules, Ed25519 cutover strategy, refresh-token reuse-detection posture) since those carry consequences no amount of frontend-code-reading can resolve. Added a "How to use this document" preamble up front: work in §9's dependency order not document order, apply recommended defaults and keep moving, only stop for the explicitly-flagged business/security items, don't invent beyond what's written or directly implied. Frontend side: confirmed nothing else is left. docs/TODO.md already has zero blockers; docs/KNOWN-ISSUES.md's one open item (unreachable Ed25519 error-code UI) is correctly left open and documented rather than faked closed - fixing it needs a real backend emitting real distinguishable error codes, which doesn't exist yet and can't be fabricated without inventing an API contract. No frontend code touched. No architecture changed. No APIs invented.
2026-07-26 16:10:33 +04:00
**Recommended default:** advisory only — word the frontend copy as
"expected back around X" not a guarantee, so an inaccurate estimate
doesn't need special-case backend handling.
- **Tenant-disabled status code and screen** — reuse `forbidden` copy (wrong
wording today) vs. add a dedicated `AuthErrorCode`.
docs: finalize BACKEND.md as a self-contained implementation prompt Goal: this document alone, pasted into a fresh Claude session, should be enough for a backend dev to implement against without getting stuck or inventing conventions the frontend doesn't actually need. Fixed a real bug: ~65 cross-references throughout the document pointed to docs/AUTHENTICATION.md, docs/ERROR_CONTRACT.md, docs/MAINTENANCE_MODE.md - three sibling docs that were deleted and fully merged into this document's own §4/§6/§10 during the earlier doc-consolidation pass, but the in-text references were never updated. A fresh agent following those links would hit dead ends repeatedly. Bulk-replaced with in- document section references; hand-fixed ~4 sentences that framed §6/ §10 as "sibling task, in progress" (stale - both are complete, this document's §6/§10 already are the settled contract, nothing to wait on). Added "Recommended default" to every item across the document's 5 consolidated "Requires backend decision" registers (§2.12 framework, §3.21 CRUD cross-cutting - 18 items, §4 §12 auth open items - 9 items, §6 error-model summary - 9 items, §10 maintenance-mode list - 5 items). Each default is derived from what the frontend already implies or standard REST/security convention - no invented APIs or business rules. ~6 items are explicitly flagged as real business/security decisions instead (order state-machine rules, Ed25519 cutover strategy, refresh-token reuse-detection posture) since those carry consequences no amount of frontend-code-reading can resolve. Added a "How to use this document" preamble up front: work in §9's dependency order not document order, apply recommended defaults and keep moving, only stop for the explicitly-flagged business/security items, don't invent beyond what's written or directly implied. Frontend side: confirmed nothing else is left. docs/TODO.md already has zero blockers; docs/KNOWN-ISSUES.md's one open item (unreachable Ed25519 error-code UI) is correctly left open and documented rather than faked closed - fixing it needs a real backend emitting real distinguishable error codes, which doesn't exist yet and can't be fabricated without inventing an API contract. No frontend code touched. No architecture changed. No APIs invented.
2026-07-26 16:10:33 +04:00
**Recommended default:** dedicated `code: "TENANT_DISABLED"` (already
named consistently elsewhere in this doc, §1.12) rather than overloading
`forbidden` — one wrong-wording bug is enough of a reason not to add a
second overload.
- **Expired-token / invalid-signature body-code fix** — both are
**pre-existing frontend bugs**, not just missing decisions:
`toAuthErrorShape()` in `auth.service.ts` currently derives the error code
from HTTP status only and ignores the `fallbackCode` for real HTTP errors,
so the `'session-expired'` and `'invalid-signature'` screens are dead code
from any real backend response today. Fixing this requires both a backend
body `error.code` and a frontend change to prefer it.
docs: finalize BACKEND.md as a self-contained implementation prompt Goal: this document alone, pasted into a fresh Claude session, should be enough for a backend dev to implement against without getting stuck or inventing conventions the frontend doesn't actually need. Fixed a real bug: ~65 cross-references throughout the document pointed to docs/AUTHENTICATION.md, docs/ERROR_CONTRACT.md, docs/MAINTENANCE_MODE.md - three sibling docs that were deleted and fully merged into this document's own §4/§6/§10 during the earlier doc-consolidation pass, but the in-text references were never updated. A fresh agent following those links would hit dead ends repeatedly. Bulk-replaced with in- document section references; hand-fixed ~4 sentences that framed §6/ §10 as "sibling task, in progress" (stale - both are complete, this document's §6/§10 already are the settled contract, nothing to wait on). Added "Recommended default" to every item across the document's 5 consolidated "Requires backend decision" registers (§2.12 framework, §3.21 CRUD cross-cutting - 18 items, §4 §12 auth open items - 9 items, §6 error-model summary - 9 items, §10 maintenance-mode list - 5 items). Each default is derived from what the frontend already implies or standard REST/security convention - no invented APIs or business rules. ~6 items are explicitly flagged as real business/security decisions instead (order state-machine rules, Ed25519 cutover strategy, refresh-token reuse-detection posture) since those carry consequences no amount of frontend-code-reading can resolve. Added a "How to use this document" preamble up front: work in §9's dependency order not document order, apply recommended defaults and keep moving, only stop for the explicitly-flagged business/security items, don't invent beyond what's written or directly implied. Frontend side: confirmed nothing else is left. docs/TODO.md already has zero blockers; docs/KNOWN-ISSUES.md's one open item (unreachable Ed25519 error-code UI) is correctly left open and documented rather than faked closed - fixing it needs a real backend emitting real distinguishable error codes, which doesn't exist yet and can't be fabricated without inventing an API contract. No frontend code touched. No architecture changed. No APIs invented.
2026-07-26 16:10:33 +04:00
**Recommended default:** backend returns `code: "TOKEN_EXPIRED"` /
`code: "INVALID_SIGNATURE"` on the relevant `401`s; frontend fix (prefer
body code over status-derived fallback) is tracked separately in
`docs/KNOWN-ISSUES.md` and is not blocking backend work — the backend
side of this can and should ship regardless of when the frontend fix
lands.
---
## 7. Uploads
Full media-upload lifecycle contract, derived from the actual media stack:
`MediaRepository` (abstract token), `MockMediaRepository` (the only impl),
`MediaLibraryFacade`, `MediaPickerComponent`, `MediaUsageService`, and the
`MediaAsset` model. **Structural status:** media is MOCK-SWAPPABLE — bound via
`{ provide: MediaRepository, useClass: MockMediaRepository }` in `app.config.ts`,
so a real HTTP repository drops in behind the same abstract class with no UI
change. **No real HTTP media backend exists yet**, and **no literal media
endpoint path exists anywhere in code** — every path below is **PROPOSED** to
satisfy the existing `MediaRepository` method shapes. The admin-auth interceptor
already attaches `AdminWebSessionID` + `Authorization: Bearer` to any `/media/`
URL (`docs/context/BACKEND-AUDIT.md` §3), so the backend can expect those headers.
### 7.1 The `MediaAsset` contract (real interface)
`src/app/core/media/models/media-asset.model.ts`:
```ts
export type MediaAssetKind = 'image' | 'svg' | 'pdf' | 'other';
export interface MediaAsset {
id: string;
url: string;
thumbnailUrl?: string;
filename: string;
mimeType: string;
size: number; // bytes
width?: number;
height?: number;
altText?: Record<string, string>; // locale -> alt text
caption?: string; // plain, non-localized
description?: string;
decorative?: boolean; // suppresses missing-alt-text warning
tags?: string[];
folder?: string;
createdAt: string; // ISO 8601
}
export interface MediaListResult { items: MediaAsset[]; total: number; }
export interface MediaListParams {
page?: number; pageSize?: number; search?: string;
folder?: string; tag?: string; kind?: MediaAssetKind; sort?: MediaSort;
}
export type MediaSort = 'recent' | 'name' | 'size';
export interface MediaUploadOptions { folder?: string; tags?: string[]; }
```
The abstract `MediaRepository` (the exact backend contract) is:
```ts
list(params?: MediaListParams): Promise<MediaListResult>;
upload(file: File, options?: MediaUploadOptions): Promise<MediaAsset>;
remove(id: string): Promise<void>;
update(id, patch: Partial<Pick<MediaAsset,
'altText'|'tags'|'folder'|'caption'|'description'|'decorative'>>): Promise<MediaAsset>;
listFolders(): Promise<string[]>;
```
### 7.2 Media upload
- **Method / path (PROPOSED):** `POST /media` (or `/media/upload`).
- **Content type:** `multipart/form-data` — the frontend holds a raw `File`
(`MediaLibraryFacade.upload(file, options)` → `repository.upload(file,
{ folder, ...options })`). The natural wire form is a multipart body with the
binary file plus `folder` and `tags[]` fields from `MediaUploadOptions`.
- **Headers:** `AdminWebSessionID` + `Authorization: Bearer <token>` (auto-added
for `/media/` by `adminAuthHeadersInterceptor`), plus multipart boundary.
- **Request:** the file blob + optional `folder: string` and `tags: string[]`.
The current folder is injected by the facade when none is passed.
- **Response:** a single fully-populated `MediaAsset` (§7.1). The backend
assigns `id`, canonical `url`, `thumbnailUrl`, `size` (post-processing),
`width`/`height`, and `createdAt`.
- **Client pre-processing to be aware of:** the mock downscales images to
2000 px and re-encodes at quality 0.85 before "upload." A real backend
receives whatever the browser sends; it should **not** assume the client
compressed anything (§5.11) and must do its own validation and any canonical
resizing.
- **Retry:** the facade keeps the last failed `File` and exposes
`retryUpload()` / `canRetry()`, and `cancelUpload()` uses an upload-token
guard to ignore a stale in-flight result. So the backend should make upload
**idempotent-safe** on retry (a retried upload after a client-perceived
failure may duplicate) — **Requires backend decision** on dedup strategy.
### 7.3 Replace
**No distinct "replace" operation exists.** `MediaRepository` has no `replace`
method; `update()` only patches metadata (`altText, tags, folder, caption,
description, decorative`), never the binary. Changing an asset's actual file =
`remove(id)` + `upload(newFile)`, producing a **new `id` and new `url`**.
**Backend implication:** references to the old asset URL (tracked by
`MediaUsageService`, §7.5) are **not** auto-updated on replace — a true
in-place replace that preserves `id`/`url` would be new frontend+backend work.
**Requires backend decision** if in-place replace is wanted.
### 7.4 Delete (single + bulk)
- **Single:** `remove(id): Promise<void>`**PROPOSED** `DELETE /media/{id}`.
`MediaLibraryFacade.remove(id)` also deselects and reloads.
- **Bulk:** `MediaLibraryFacade.bulkDelete(ids[])` loops `repository.remove(id)`
**one call per id** (no batch endpoint) — so bulk delete today is N ×
`DELETE /media/{id}`. The confirm-dialog / selection flow (RC-02) gates this
in the UI; the backend just sees serial deletes. **Requires backend decision:**
whether to add a batch `DELETE /media` (body `{ ids: [] }`) to avoid N
round-trips — the facade would need a matching `bulkRemove` method (not
present today).
- There is also `bulkMoveToFolder(ids, folder)` = N × `update(id, { folder })`.
- **Hard vs soft delete: Requires backend decision** — the frontend calls
`remove` and expects the asset gone from `list()`; it has no restore/trash
concept for media (unlike admin orders/categories which do have
archive/restore).
### 7.5 Metadata
`MediaAsset` carries this metadata (all editable via `update()` except the
system-assigned fields):
| Field | Editable by frontend? | Notes |
|---|---|---|
| `altText: Record<string,string>` | yes | **Per-locale** alt text (locale key → string). |
| `caption` | yes | Plain, non-localized. |
| `description` | yes | Plain. |
| `decorative: boolean` | yes | Suppresses the missing-alt warning. |
| `tags: string[]` | yes | Free-form; drives the tag filter. |
| `folder: string` | yes | Single folder; `listFolders()` derives the folder list. |
| `width` / `height` | no (system) | Set from image dimensions at upload. |
| `size` | no (system) | Bytes, post-processing. |
| `mimeType` | no (system) | |
| `filename` | no (system) | Original filename. |
| `createdAt` | no (system) | ISO 8601 upload timestamp. |
There is **no `uploader`/`updatedAt`/usage-count field** on `MediaAsset`.
Usage is computed **client-side on demand** by `MediaUsageService.findUsages(bootstrap, url)`
(`src/app/core/media/media-usage.service.ts`) — it walks the in-memory
`BootstrapConfig` looking for the asset's `url` as a string value and reports
where it is referenced (branding, theme, header, footer, static pages, etc.).
So "usage tracking" is a read-time scan of the config, **not** a stored
reverse-index. **Requires backend decision:** whether the backend should track
uploader identity and a stored usage/reference count (the frontend does not
require either today, but `metadata`-export and audit (§5.14) would benefit).
**Metadata update endpoint (PROPOSED):** `PATCH /media/{id}` with the
`update()` patch body, returning the updated `MediaAsset`.
### 7.6 Image variants
The frontend **does not use responsive image variants**. Zero `srcset`/`sizes`
usage in Angular templates (the only `srcset`-ish hit is unrelated). Images are
consumed as a **single `url`**, with an optional single `thumbnailUrl`. In the
mock, `thumbnailUrl === url` (same object URL) — there is no distinct thumbnail
rendition today. **Backend implication:** the contract is single-`url` +
optional single-`thumbnailUrl`; the frontend will happily consume a
backend-generated thumbnail if `thumbnailUrl` differs from `url`, but it never
requests a specific size or a `srcset` set. Adding true responsive variants
would be new frontend work.
### 7.7 Thumbnail generation
**Requires backend decision** on strategy — nothing in the frontend prescribes
sizes or formats. But the frontend gives a concrete **display target** so the
backend has a real number to aim at:
- The media library / picker renders a **grid** whose cells are ~150 px wide
(`MediaPickerComponent.onGridKeydown` computes columns as
`clientWidth / 150`), page size **24** items.
- A `thumbnailUrl` sized around **~150300 px** (accounting for 2× DPI) would
serve the grid without shipping full-resolution originals.
If the backend populates `thumbnailUrl` with such a rendition, the UI uses it
automatically (falls back to `url` when absent). No other thumbnail sizes are
consumed anywhere.
### 7.8 Limits
- **Per-file size:** 10 MB, client-enforced (§5.12) — the backend must
re-enforce (§5.11).
- **MIME allowlist:** the 6 types in §5.13.
- **Upload count / batch size:** no client cap — `bulkDelete`/`bulkMoveToFolder`
loop over arbitrary-length id arrays; single-file upload only (the picker's
`<input>` takes `files?.[0]`, so **one file per upload call**).
- **Storage quota (per tenant / per account):** none client-side —
**Requires backend decision**.
- **Pagination:** `pageSize` default **24** (`DEFAULT_PAGE_SIZE`/`PAGE_SIZE`);
`list()` returns `{ items, total }` so the backend must return an accurate
`total` for the pager.
### 7.9 Future S3 compatibility
The `MediaAsset`/`MediaRepository` contract is deliberately **storage-agnostic**
and this is a **design constraint for the backend, not a frontend requirement**:
the frontend only ever sees opaque `url` / `thumbnailUrl` strings and an opaque
`id`. It never constructs storage paths, never assumes a host, and never signs
or negotiates storage directly. Therefore the backend is free to back media
with S3, a CDN, local disk, or anything else — including returning
pre-signed/CDN URLs in `url`/`thumbnailUrl` — with **no frontend change**, as
long as:
- `upload()` still returns a complete `MediaAsset` synchronously with a usable
`url` (a direct-to-S3 pre-signed **upload** flow, where the client PUTs to S3
itself, is **not** what the current `repository.upload(file)` shape expects —
it passes the `File` to the repository and awaits the finished asset;
switching to browser-direct-to-S3 would be new frontend work). **Requires
backend decision** if a pre-signed direct-upload flow is desired.
- Served asset URLs are treated as untrusted-origin content (§5.11) —
isolated origin, `nosniff`, correct `Content-Type`/`Content-Disposition`.
---
## 8. Real Backend Implementation Guide
This section is the practical, top-to-bottom guide a backend engineer follows to replace the
frontend's mock/local data layer with real endpoints. It is derived entirely from
`docs/context/BACKEND-AUDIT.md` (the exhaustive source audit) — no business logic is invented
beyond what the frontend gateway interfaces already fix. Where the frontend has no seam or no
mapper today, that gap is stated explicitly rather than papered over.
The core principle, already proven for Categories, is: **the gateway/provider _interface_ is the
contract; a real implementation only has to satisfy that interface.** You never rewrite the
interface, and — if the domain already has a DI-token seam — you never touch the facade or the
UI. The only structural surprise, called out repeatedly below, is that **most admin domains do
not have that seam yet** (audit §1, §14, §25 #1): their facade injects the concrete
`Admin*LocalGateway` class directly, so a token must be introduced before a real gateway can be
bound at all.
### 8.1 Which frontend gateways must be replaced
Every data boundary the audit found, with its swap status. "DI token status" is the single most
important column — it tells you whether you can drop in a real class (`already token-bound`) or
must first introduce a seam (`needs token added first`).
| Gateway / provider interface | Current mock/local impl file | DI token status | Real impl class to write | Consuming facade(s) |
|---|---|---|---|---|
| `ConfigProvider` | `core/bootstrap/providers/mock-bootstrap.provider.ts` | already token-bound (`CONFIG_PROVIDER`) | **already exists & LIVE**`ApiBootstrapProvider` (`GET /bootstrap`) | UiRuntime, WebsiteRuntime, ProjectEditor, ContentManagement, Diagnostics (via `ConfigService`) |
| `ProductDataProvider` | none bound (mock via `mockDataInterceptor` only) | already token-bound (`PRODUCT_DATA_PROVIDER`) | **already exists & LIVE**`ApiProductDataProvider` | `ProductFacade` |
| `CategoryRepository` | none bound (mock via interceptor) | already token-bound (`CATEGORY_REPOSITORY`) | **already exists & LIVE**`ApiCategoryRepository` (`GET /category`) | `CategoryFacade` |
| `BackofficeDataProvider` | `core/backoffice/providers/mock-backoffice-data.provider.ts` | already token-bound (`BACKOFFICE_DATA_PROVIDER`) | **already exists & LIVE**`ApiBackofficeDataProvider` (`/api/backoffice/*`) | storefront cards |
| `AdminCategoriesGateway` | `features/admin/categories/services/admin-categories-local.gateway.ts` | already token-bound (`ADMIN_CATEGORIES_GATEWAY`) | **already exists & LIVE**`AdminCategoriesApiGateway` | `AdminCategoriesFacade`, `AdminAnalyticsFacade` |
| `AdminDashboardMetricsGateway` | `features/admin/dashboard/services/admin-dashboard-metrics.local.gateway.ts` | already token-bound (`ADMIN_DASHBOARD_METRICS_GATEWAY`) | `AdminDashboardMetricsApiGateway` (new) | `AdminDashboardFacade` |
| `MediaRepository` (abstract class) | `core/media/mock-media-repository.service.ts` | already token-bound (abstract-class token in `app.config.ts`) | `ApiMediaRepository` (new) | `MediaLibraryFacade` |
| `UserExperienceRepository` | `core/user-experience/repositories/local-user-experience.repository.ts` | already token-bound (`USER_EXPERIENCE_REPOSITORY`) | `AuthenticatedUserExperienceRepository` (new; see §8.3 note on redesign) | `UserExperienceFacade` |
| `AdminOrdersGateway` | `features/admin/orders/services/admin-orders-local.gateway.ts` | **needs token added first** | `AdminOrdersApiGateway` (new) | `AdminOrdersFacade`, `AdminCustomersFacade`, `AdminAnalyticsFacade` |
| `AdminProductsGateway` | `features/admin/products/services/admin-products-local.gateway.ts` | **needs token added first** | `AdminProductsApiGateway` (new) | `AdminProductsFacade`, `AdminAnalyticsFacade` |
| `AdminUsersGateway` | `features/admin/users/services/admin-users-local.gateway.ts` | **needs token added first** | `AdminUsersApiGateway` (new) | `AdminUsersFacade` |
| `AdminTransactionsGateway` | `features/admin/transactions/services/admin-transactions-local.gateway.ts` | **needs token added first** | `AdminTransactionsApiGateway` (new) | `AdminTransactionsFacade` |
| `AdminMonitoringGateway` | `features/admin/monitoring/services/admin-monitoring-local.gateway.ts` | **needs token added first** | `AdminMonitoringApiGateway` (new) | `AdminMonitoringFacade` |
| `AdminModerationGateway` | `features/admin/moderation/services/admin-moderation-local.gateway.ts` | **needs token added first** | `AdminModerationApiGateway` (new) | `AdminModerationFacade`, `AdminAnalyticsFacade` |
| (Customers) | reuses `AdminOrdersLocalGateway` (derived) | **needs token added first** (inherits Orders seam) | none of its own — derive from `AdminOrdersApiGateway`, or add a `/customers` source | `AdminCustomersFacade` |
| (Analytics) | reuses orders/products/moderation local + `ADMIN_CATEGORIES_GATEWAY` + `AdminDashboardFacade` | partial (categories/dashboard token-bound; rest not) | no aggregation endpoint exists — see §8.3 | `AdminAnalyticsFacade` |
| Auth session (`TelegramSessionApiService`) | mock via `mockDataInterceptor` | n/a (concrete service, LIVE) | **already exists & LIVE** — no swap; implement server endpoints only | `AuthService`, `AdminAuthService`, `AuthFacade` |
| Ed25519 admin auth (`AuthApiService`) | none | n/a (concrete service, LIVE wiring) | **client wiring exists; backend absent** — implement `/api/admin/auth/*` server-side | `AuthService` (Ed25519 flow) |
| `SearchHistoryRepository` | localStorage impl (concrete) | injected concretely — LOCAL-ONLY by design | — (leave local; no backend) | `SearchFacade` (via `SearchHistoryService`) |
Read this table as three tiers:
1. **Done / LIVE (no gateway work):** ConfigProvider, ProductDataProvider, CategoryRepository,
BackofficeDataProvider, AdminCategories. These already talk to real HTTP through a real
`*Api*` class. The backend job here is only to _stand up the server endpoints_ these clients
already call — not to touch frontend code.
2. **Token-bound, real impl missing:** DashboardMetrics, Media, UserExperience. The seam exists;
write the `*Api*` class and bind it. No facade edit.
3. **No seam yet (the bulk of admin CRUD):** Orders, Products, Users, Transactions, Monitoring,
Moderation (plus derived Customers/Analytics). Each needs a token introduced _and_ a facade
injection change _before_ a real gateway can even be bound.
### 8.2 Interfaces that stay unchanged (do not touch)
The gateway/provider interface files _are_ the frozen seam. A real implementation satisfies the
existing TypeScript interface; changing an interface is a frontend contract change and forces UI
churn. **Do not edit any of these** while wiring a backend:
- `core/config/config-provider.interface.ts` (`ConfigProvider`)
- `core/products/providers/product-data-provider.interface.ts` (`ProductDataProvider`)
- `core/categories/repositories/category.repository.ts` (`CategoryRepository`)
- `core/backoffice/providers/backoffice-data-provider.interface.ts` (`BackofficeDataProvider`)
- `core/media/media-repository.ts` (abstract class `MediaRepository`)
- `core/user-experience/repositories/user-experience.repository.ts` (`UserExperienceRepository`)
- `features/admin/categories/services/admin-categories-gateway.interface.ts` (`AdminCategoriesGateway`)
- `features/admin/dashboard/services/admin-dashboard-metrics.gateway.interface.ts` (`AdminDashboardMetricsGateway`)
- `features/admin/orders/services/admin-orders-gateway.interface.ts` (`AdminOrdersGateway`)
- `features/admin/products/services/admin-products-gateway.interface.ts` (`AdminProductsGateway`)
- `features/admin/users/services/admin-users-gateway.interface.ts` (`AdminUsersGateway`)
- `features/admin/transactions/services/admin-transactions-gateway.interface.ts` (`AdminTransactionsGateway`)
- `features/admin/monitoring/services/admin-monitoring-gateway.interface.ts` (`AdminMonitoringGateway`)
- `features/admin/moderation/services/admin-moderation-gateway.interface.ts` (`AdminModerationGateway`)
The interface method lists (the shapes the backend must satisfy) are enumerated in audit §14 and
must not drift. If the backend genuinely cannot meet a method's shape, that is a conversation to
have with the frontend owner and change the interface deliberately — not something to work around
inside a mapper.
### 8.3 Facades: which stay unchanged, which need a small adjustment
The facade is the abstraction the components consume. **If a domain already has a DI-token seam
and the real gateway honors the interface, its facade needs zero changes.** That covers all of
tier 1 and tier 2 above (ConfigProvider/Product/Category/Backoffice/AdminCategories/Dashboard/
Media/UserExperience facades are all injection-by-token already).
Facades that **do** need a (small, mechanical) change:
- **`AdminOrdersFacade`, `AdminProductsFacade`, `AdminUsersFacade`, `AdminTransactionsFacade`,
`AdminMonitoringFacade`, `AdminModerationFacade`** — each currently injects the concrete
`Admin*LocalGateway` class directly (audit §14, §21). The one-line change is to inject the new
DI token instead of the concrete class. This is unavoidable for these six because the seam does
not exist yet; it is the "add a token seam" half of the migration, not a behavior change.
Facades whose current behavior is **mock-shaped** and needs more than a wiring swap:
- **`AdminCustomersFacade`** (`features/admin/customers/facade/…`) derives its customer list by
reading `AdminOrdersLocalGateway` in memory (audit §14, §21). Once orders are real, it can keep
deriving customers from the Orders _token_, but if the backend exposes a first-class customers
source, prefer that. Either way the "derive from local orders" assumption is mock-specific and
should be revisited when Orders goes real.
- **`AdminAnalyticsFacade`** (`features/admin/analytics/facade/…`) composes orders + products +
moderation local gateways plus the categories/dashboard tokens (audit §14, §21). There is **no
analytics aggregation endpoint and no tracking pipeline today** (audit §14; remaining-work
#13/#17). Real analytics is not a gateway swap — it needs a data source that does not exist yet.
Treat it as the last domain, dependent on orders/products/moderation all being real first.
- **`UserExperienceFacade`** (`facades/platform/user-experience.facade.ts`) stores fully
denormalized wishlist/compare objects synchronously in localStorage (audit §19). The backend
decision (remaining-work #16) is id-only sync + a `GET /items/batch?ids=` hydration endpoint,
which requires redesigning the repository to store id-arrays and hydrate from a local product
cache — a real refactor, not a drop-in `*Api*` class. Flagged so nobody assumes the token swap
alone finishes wishlist sync.
- **`ContentManagementFacade`** and **`ProjectEditorFacade`** have **no backend call at all**
today (audit §16, §17). They read/mutate the in-memory `BootstrapConfig` and persist drafts to
localStorage; "publishing a marketplace" = writing bootstrap back, for which **no client write
call exists**. Wiring these to a real backend means _adding_ a draft/publish HTTP path
(`ProjectEditorIoService` currently only does JSON.stringify/parse; drafts live in
`ProjectEditorDraftStorageService` / localStorage), not swapping a gateway.
- **Cart** (`CartService`) is local-only by design (localStorage `marketplace_cart`); checkout
already emits real `POST /cart` / `POST /orders` calls (audit §10). No facade change — just be
aware cart _contents_ never round-trip to a backend.
Also note the mock gateways commonly apply an **artificial delay** and keep state in
localStorage/in-memory overlays (audit §14). A real gateway should drop the fake latency; the
facades tolerate real async already (they're signal/Observable-based), so no facade edit is needed
for that specifically.
### 8.4 DTO mapping examples (worked)
The frontend already carries adapters for its LIVE domains; a backend engineer should treat those
adapters as the **tolerance contract** for the wire shape. Three representative cases:
#### Example A — Categories (clean stack, mapper already exists)
Real backend JSON (`GET /category`, shape the client already tolerates):
```json
[
{
"categoryID": 12,
"names": [ { "lang": "ru", "name": "Электроника" }, { "lang": "en", "name": "Electronics" } ],
"subcategories": [ { "categoryID": 34, "names": [ { "lang": "en", "name": "Phones" } ] } ]
}
]
```
Frontend wire DTO: `CategoryDto`, `CategoryNameDto` (`core/categories/dto/category.dto.ts`).
Domain model: `Category`, `CategoryTranslation` (`core/categories/models/category-domain.model.ts`).
Bridge: **`CategoryMapper`** (`core/categories/mappers/category.mapper.ts`) — flattens the
subcategory tree, dedupes by id, and normalizes the language code `am → hy`. **Already written.**
Backend action: match the `CategoryDto` field names; no new mapper.
#### Example B — Products (largest existing mapper; match its tolerance)
Real backend JSON (`GET /items/{id}`) → frontend `Item` (`src/app/models/item.model.ts`).
Bridge: **`ApiService.normalizeItem()`** — the single largest inline adapter in the codebase
(audit §7). It reconciles two historical shapes and normalizes, among others:
- `id` (string) ↔ `itemID` (numeric)
- `imgs[]``photos[]` (→ `ProductMedia`)
- `names[]``translations`
- `description` as key/value array ↔ string
- `comments``callbacks` (reviews)
- color `0xRRGGBB``#RRGGBB`; `remaining` count → stock band
Backend action: **do not "clean up" these dual shapes**`normalizeItem`/`normalizeCategory`
define exactly what the client accepts. Producing a payload inside that tolerance envelope is the
contract; a stricter/renamed shape breaks the storefront. No new mapper needed — the adapter
exists and is LIVE.
#### Example C — Admin Orders (no mapper exists yet — one must be written)
Unlike A and B, the admin domains have **no wire DTO and no mapper** — the local gateways
construct `AdminOrder` view models directly in memory and never touch a URL (audit §14, §24).
So this is the shape you have _freedom_ to design on the backend, as long as the new
`AdminOrdersApiGateway` maps it into the existing view model.
Proposed backend JSON (`GET /backoffice/orders/:id`):
```json
{
"id": "ord_1042",
"status": "processing",
"paymentStatus": "paid",
"customer": { "id": "cus_88", "name": "…", "email": "…" },
"items": [ { "productId": "…", "title": "…", "qty": 2, "unitPrice": 1990 } ],
"shipping": { "method": "…", "address": "…" },
"timeline": [ { "event": "created", "at": "2026-07-01T10:00:00Z" } ]
}
```
Frontend view model: `AdminOrder` + `AdminOrderCustomer`, `AdminOrderPayment`,
`AdminOrderShipping`, `AdminOrderItem`, `AdminOrderTimelineEntry`, `AdminOrderStatus`,
`AdminOrderPaymentStatus` (`features/admin/orders/models/admin-order.model.ts`, audit §23).
Bridge: **a new `AdminOrdersApiGateway` must contain the mapping** JSON → `AdminOrder`, honoring
the `AdminOrdersGateway` interface methods (`loadOrders`, `loadOrder`, `updateStatus`,
`requestRefund`, `addNote`, `archiveOrder`, `restoreOrder`, `deleteOrder`). The `status` field
docs: final project closeout - classify TODO, backend spec, status Classified every TODO.md item into one of DONE/BACKEND/PRODUCT DECISION/FUTURE VERSION/BUG, verified against source, not against prior docs: - BACKEND items (bootstrap content, builder draft/publish, 6 admin CRUD domains, media pipeline) confirmed already covered by BACKEND_INTEGRATION.md; appended a mapping appendix rather than duplicating raw bullets. Fixed 22 stale internal BACKEND_API.md cross-references left over from before that file was archived. - PRODUCT DECISION items (dark mode, brand-color WCAG contrast, stars.component token gap, footer Contacts content, advanced analytics, payment providers) moved to new docs/PRODUCT_BACKLOG.md. - FUTURE VERSION items (Angular 22, bundle splitting, cart-modal composition cleanup, hero-spacing investigation) moved to new docs/FUTURE_FEATURES.md. - BUG: rewrote docs/KNOWN-ISSUES.md down to the one real, verified, currently-reproducible frontend bug (Ed25519 admin-auth error codes session-expired/invalid-signature are unreachable - toAuthErrorShape() never reads a body error code, only maps HTTP status, and no status ever produces those two codes - confirmed by reading auth.service.ts + auth-error.model.ts). Condensed the "Fixed" history instead of carrying full verbose repro text forward. - DONE items removed outright (dead-code deletion, dashboard false positive, RC-02 fixes, stale "dynamic-renderer unwired"/"178 missing keys" claims already disproven by source). docs/TODO.md rewritten to the exact "no blockers" template - nothing left qualifies as a release blocker. New docs/PROJECT_STATUS.md: honest per-area status (frontend/backend/ docs/auth/builder/storefront/admin), known limitations, and explicit production/backend/demo readiness calls - including correcting an initial draft's unpushed-commit count (53, not 10, per git log origin/B2B..HEAD). New docs/NEXT_PHASE.md: work that can only start once a real backend exists (gateway swap-in, mock removal, dormant-auth activation, role enforcement, integration/E2E tests, perf profiling, monitoring, maintenance-mode UI). docs/PROJECT_INDEX.md (the stated entry point) updated to link the new doc set and stop pointing at the now-archived BACKEND_API.md/AUTH.md. docs/FRONTEND-ROADMAP.md's "Known open items" replaced with pointers to the new category-split docs instead of a duplicated mixed list. Not swept: a handful of low-traffic docs (architecture ADRs, FRONTEND.md, EDITOR.md, ARCHITECTURE.md, PROJECT-STRUCTURE.md, StaticPages.md, ADMIN.md) still reference the old BACKEND_API.md/ AUTH.md filenames - noted as a known gap in PROJECT_STATUS.md rather than touched blindly, since they're historical-context docs, not the navigation entry point.
2026-07-26 12:35:26 +04:00
must respect the order state machine (see the Orders CRUD contract / archive/BACKEND_API.md §8.1).
The same "no mapper exists, write one inside the new `*ApiGateway`" note applies to Products,
Users, Transactions, Monitoring, and Moderation.
### 8.5 Per-domain migration checklist (the pattern, then the table)
**Generic pattern to flip one domain from mock to real.** Steps 13 are only needed for the
"no seam yet" domains; token-bound domains start at step 4.
1. **Ensure the gateway interface exists** (all admin domains already have one — audit §14). If a
domain truly has none (e.g. Customers, Analytics derive from others), decide whether to add one
or keep deriving.
2. **Add a DI token**`InjectionToken` + factory that selects mock vs. real off
`RuntimeProviderStrategyService`, mirroring `admin-categories-gateway.token.ts`.
3. **Update the facade injection** from the concrete `Admin*LocalGateway` class to the new token.
4. **Implement the `*ApiGateway`** class satisfying the existing interface (contains the DTO→view
mapper, per §8.4 Example C).
5. **Bind the token to the real impl** in the providers (factory returns the api gateway for
`api` mode).
6. **Retire the `*LocalGateway`** — either delete it or keep it behind the existing
`useMockData` / mode flag as a dev fixture (the factory already lets both coexist).
This is the exact pattern Categories already follows (`admin-categories-api.gateway.ts` +
`admin-categories-gateway.token.ts`); replicate it verbatim.
| Domain | Needs steps 13 (add seam)? | Interface exists? | Real impl exists? | Net work |
|---|---|---|---|---|
| Categories (admin) | no (token-bound) | yes | **yes** | server endpoints only |
| Dashboard metrics | no (token-bound) | yes | no | steps 46 |
| Media | no (token-bound, class token) | yes (abstract class) | no | steps 46 |
| User experience | no (token-bound) | yes | no | steps 46 **+ repo redesign** (id-only + batch hydrate) |
| Orders | **yes** | yes | no | steps 16 (skip 1) |
| Products (admin) | **yes** | yes | no | steps 16 (skip 1) |
| Users / roles | **yes** | yes | no | steps 16 (skip 1) |
| Transactions | **yes** | yes | no | steps 16 (skip 1) |
| Monitoring | **yes** | yes | no | steps 16 (skip 1) |
| Moderation | **yes** | yes | no | steps 16 (skip 1) |
| Customers | inherits Orders seam | no (derived) | no | derive from Orders token, or add source |
| Analytics | partial | no (derived) | no | needs data source first (last) |
| Content management | n/a (no gateway) | n/a | no | **add** draft/publish HTTP path |
| Project editor / builder | n/a (no gateway) | n/a | no | **add** draft/publish HTTP path |
### 8.6 Recommended backend implementation order (all domains)
Ordered by the frontend's actual dependency structure (audit §5§18), not by convenience:
1. **Auth + session first.** `TelegramSessionApiService` is LIVE and every admin path is gated by
the `adminAuthHeadersInterceptor` (`AdminWebSessionID` / `Bearer`, audit §3). Nothing
role-gated works until session issuance and (if used) the Ed25519 `/api/admin/auth/*` flow are
real. Blocks everything admin.
2. **Bootstrap / tenant resolution.** `GET /bootstrap` transport is done, but its **content**
(branding/theme/nav/seo) is still stubbed (remaining-work #1). Tenant resolution and
`ApiConfigService.getBaseUrl()` depend on it; every tenant-scoped call resolves through it.
3. **Categories.** Already LIVE for both storefront (`GET /category`) and admin
(`AdminCategoriesApiGateway`). Products reference categories, so categories must be real and
populated before products are meaningful. Mostly "confirm server endpoints" work.
4. **Products / catalog.** Storefront reads (`GET /items`, `/searchitems`, `/randomitems`) are
LIVE; the **admin Products CRUD** is the first no-seam domain to build (remaining-work #3, P0),
and it depends on categories.
5. **Media.** Products/categories editors reference media assets; a real `ApiMediaRepository`
(remaining-work #5, P0) should land alongside/just after admin Products.
6. **Cart / Orders / Transactions.** Checkout `POST /cart` + `POST /orders` are LIVE; admin
**Orders CRUD** (remaining-work #6) then **Transactions** (tied to orders, #7) are the P1 core
commerce domains. Orders before Transactions/Customers/Analytics (they derive from it).
7. **Reviews / Moderation.** Customer review/question writes are LIVE; admin **Moderation**
(#10) gates them. Depends on products existing.
8. **Users / roles / invitations** (#9) — admin governance; independent of commerce but needs
auth (step 1).
9. **Dashboard metrics** (#11) then **Monitoring** (#12) — operational visibility over the
domains above.
10. **Analytics** (#13/#17) — **last**: needs orders/products/moderation real _and_ a tracking
pipeline that does not exist yet.
11. **Builder draft/publish + Content/CMS** (#2 P0 for builder transport, #14 for CMS) — these are
net-new write paths (no client call exists, audit §16/§17). Builder publish is high priority
for the builder product but is orthogonal to the storefront/commerce chain, so it can proceed
in parallel once bootstrap content (step 2) is real.
12. **User-experience sync** (#16) and **search suggestions/filters** (#15) — P2/P3 enhancements
over already-working local features.
### 8.7 Estimated effort (frontend-informed)
These are **frontend-informed** sizes only — they reflect how much of the contract is already
seamed/mapped on the client. Backend has its own unknowns (schema design, storage, infra, the
tracking pipeline) that are **out of scope for this document**; a domain marked S here can still be
L on the server.
| Domain | Size | Why |
|---|---|---|
| Categories (storefront + admin) | **S** | LIVE both sides; `CategoryMapper` + `AdminCategoriesApiGateway` exist. Server endpoints only. |
| Products storefront reads | **S** | LIVE; `normalizeItem` absorbs shape variance. |
| Bootstrap content | **M** | Transport done; producing real branding/theme/nav/seo values is the work (#1). |
| Media | **M** | Token seam + abstract interface ready; write `ApiMediaRepository` + upload pipeline (#5). |
| Admin Orders | **M** | Interface + rich view models exist, but no seam and no mapper — steps 16 + mapper. |
| Admin Products CRUD + variants | **L** | No seam, no mapper; largest admin model set (variants/attributes/translations/seo). |
| Admin Users/roles/invitations | **M** | No seam; also reconcile the duplicate `AdminRole` naming (audit §14/§25 #3). |
| Admin Transactions | **M** | No seam; tied to Orders shape. |
| Admin Moderation | **M** | No seam; two state machines (reviews + reports). |
| Admin Monitoring | **M** | No seam; three sub-resources (events/queues/webhooks). |
| Dashboard metrics | **SM** | Token-bound already; single `loadMetrics` shape. |
| Customers | **S** | Derives from Orders once Orders is real. |
| Analytics | **XL** | No data source; needs tracking pipeline + aggregation, gated on everything above. |
| User-experience sync | **L** | Requires client repository redesign (id-only + batch hydrate), not a drop-in. |
| Builder draft/publish | **L** | Net-new write path; no client call exists today. |
| Content management / CMS | **ML** | Net-new write path; adapter (`ContentPageService`) exists but no HTTP. |
| Search suggestions/filters | **M** | Client orchestration exists; backend suggestion source is new. |
---
## 9. Backend Checklist
A literal, top-to-bottom checklist. Work the phases in order; within a phase, items are roughly
independent. Section references point to the assembled backend-integration document (this doc's
docs: final project closeout - classify TODO, backend spec, status Classified every TODO.md item into one of DONE/BACKEND/PRODUCT DECISION/FUTURE VERSION/BUG, verified against source, not against prior docs: - BACKEND items (bootstrap content, builder draft/publish, 6 admin CRUD domains, media pipeline) confirmed already covered by BACKEND_INTEGRATION.md; appended a mapping appendix rather than duplicating raw bullets. Fixed 22 stale internal BACKEND_API.md cross-references left over from before that file was archived. - PRODUCT DECISION items (dark mode, brand-color WCAG contrast, stars.component token gap, footer Contacts content, advanced analytics, payment providers) moved to new docs/PRODUCT_BACKLOG.md. - FUTURE VERSION items (Angular 22, bundle splitting, cart-modal composition cleanup, hero-spacing investigation) moved to new docs/FUTURE_FEATURES.md. - BUG: rewrote docs/KNOWN-ISSUES.md down to the one real, verified, currently-reproducible frontend bug (Ed25519 admin-auth error codes session-expired/invalid-signature are unreachable - toAuthErrorShape() never reads a body error code, only maps HTTP status, and no status ever produces those two codes - confirmed by reading auth.service.ts + auth-error.model.ts). Condensed the "Fixed" history instead of carrying full verbose repro text forward. - DONE items removed outright (dead-code deletion, dashboard false positive, RC-02 fixes, stale "dynamic-renderer unwired"/"178 missing keys" claims already disproven by source). docs/TODO.md rewritten to the exact "no blockers" template - nothing left qualifies as a release blocker. New docs/PROJECT_STATUS.md: honest per-area status (frontend/backend/ docs/auth/builder/storefront/admin), known limitations, and explicit production/backend/demo readiness calls - including correcting an initial draft's unpushed-commit count (53, not 10, per git log origin/B2B..HEAD). New docs/NEXT_PHASE.md: work that can only start once a real backend exists (gateway swap-in, mock removal, dormant-auth activation, role enforcement, integration/E2E tests, perf profiling, monitoring, maintenance-mode UI). docs/PROJECT_INDEX.md (the stated entry point) updated to link the new doc set and stop pointing at the now-archived BACKEND_API.md/AUTH.md. docs/FRONTEND-ROADMAP.md's "Known open items" replaced with pointers to the new category-split docs instead of a duplicated mixed list. Not swept: a handful of low-traffic docs (architecture ADRs, FRONTEND.md, EDITOR.md, ARCHITECTURE.md, PROJECT-STRUCTURE.md, StaticPages.md, ADMIN.md) still reference the old BACKEND_API.md/ AUTH.md filenames - noted as a known gap in PROJECT_STATUS.md rather than touched blindly, since they're historical-context docs, not the navigation entry point.
2026-07-26 12:35:26 +04:00
§8, the CRUD-contracts sections, and `docs/archive/BACKEND_API.md` where a full shape already lives).
### Phase 1 — Foundation (nothing role-gated works until these land)
docs: final project closeout - classify TODO, backend spec, status Classified every TODO.md item into one of DONE/BACKEND/PRODUCT DECISION/FUTURE VERSION/BUG, verified against source, not against prior docs: - BACKEND items (bootstrap content, builder draft/publish, 6 admin CRUD domains, media pipeline) confirmed already covered by BACKEND_INTEGRATION.md; appended a mapping appendix rather than duplicating raw bullets. Fixed 22 stale internal BACKEND_API.md cross-references left over from before that file was archived. - PRODUCT DECISION items (dark mode, brand-color WCAG contrast, stars.component token gap, footer Contacts content, advanced analytics, payment providers) moved to new docs/PRODUCT_BACKLOG.md. - FUTURE VERSION items (Angular 22, bundle splitting, cart-modal composition cleanup, hero-spacing investigation) moved to new docs/FUTURE_FEATURES.md. - BUG: rewrote docs/KNOWN-ISSUES.md down to the one real, verified, currently-reproducible frontend bug (Ed25519 admin-auth error codes session-expired/invalid-signature are unreachable - toAuthErrorShape() never reads a body error code, only maps HTTP status, and no status ever produces those two codes - confirmed by reading auth.service.ts + auth-error.model.ts). Condensed the "Fixed" history instead of carrying full verbose repro text forward. - DONE items removed outright (dead-code deletion, dashboard false positive, RC-02 fixes, stale "dynamic-renderer unwired"/"178 missing keys" claims already disproven by source). docs/TODO.md rewritten to the exact "no blockers" template - nothing left qualifies as a release blocker. New docs/PROJECT_STATUS.md: honest per-area status (frontend/backend/ docs/auth/builder/storefront/admin), known limitations, and explicit production/backend/demo readiness calls - including correcting an initial draft's unpushed-commit count (53, not 10, per git log origin/B2B..HEAD). New docs/NEXT_PHASE.md: work that can only start once a real backend exists (gateway swap-in, mock removal, dormant-auth activation, role enforcement, integration/E2E tests, perf profiling, monitoring, maintenance-mode UI). docs/PROJECT_INDEX.md (the stated entry point) updated to link the new doc set and stop pointing at the now-archived BACKEND_API.md/AUTH.md. docs/FRONTEND-ROADMAP.md's "Known open items" replaced with pointers to the new category-split docs instead of a duplicated mixed list. Not swept: a handful of low-traffic docs (architecture ADRs, FRONTEND.md, EDITOR.md, ARCHITECTURE.md, PROJECT-STRUCTURE.md, StaticPages.md, ADMIN.md) still reference the old BACKEND_API.md/ AUTH.md filenames - noted as a known gap in PROJECT_STATUS.md rather than touched blindly, since they're historical-context docs, not the navigation entry point.
2026-07-26 12:35:26 +04:00
- [ ] Implement session issuance/check/logout: `POST /users/sessions`, `GET /users/sessions/:id`, `DELETE /users/sessions/:id` — per Auth contract (§5a; archive/BACKEND_API.md `/users/sessions/*`, already client-LIVE).
- [ ] Implement the Ed25519 admin-auth flow `GET /api/admin/auth/challenge`, `POST /verify`, `POST /refresh`, `POST /logout` — client wiring is LIVE and 404s today (§5b). Return `AuthChallenge` / `AuthTokenPair` shapes exactly.
- [ ] Honor the admin auth headers on every gated path: `AdminWebSessionID` + `Authorization: Bearer` for URLs containing `/admin/`, `/backoffice/`, `/builder/`, `/media/` (§3 interceptor pipeline).
docs: final project closeout - classify TODO, backend spec, status Classified every TODO.md item into one of DONE/BACKEND/PRODUCT DECISION/FUTURE VERSION/BUG, verified against source, not against prior docs: - BACKEND items (bootstrap content, builder draft/publish, 6 admin CRUD domains, media pipeline) confirmed already covered by BACKEND_INTEGRATION.md; appended a mapping appendix rather than duplicating raw bullets. Fixed 22 stale internal BACKEND_API.md cross-references left over from before that file was archived. - PRODUCT DECISION items (dark mode, brand-color WCAG contrast, stars.component token gap, footer Contacts content, advanced analytics, payment providers) moved to new docs/PRODUCT_BACKLOG.md. - FUTURE VERSION items (Angular 22, bundle splitting, cart-modal composition cleanup, hero-spacing investigation) moved to new docs/FUTURE_FEATURES.md. - BUG: rewrote docs/KNOWN-ISSUES.md down to the one real, verified, currently-reproducible frontend bug (Ed25519 admin-auth error codes session-expired/invalid-signature are unreachable - toAuthErrorShape() never reads a body error code, only maps HTTP status, and no status ever produces those two codes - confirmed by reading auth.service.ts + auth-error.model.ts). Condensed the "Fixed" history instead of carrying full verbose repro text forward. - DONE items removed outright (dead-code deletion, dashboard false positive, RC-02 fixes, stale "dynamic-renderer unwired"/"178 missing keys" claims already disproven by source). docs/TODO.md rewritten to the exact "no blockers" template - nothing left qualifies as a release blocker. New docs/PROJECT_STATUS.md: honest per-area status (frontend/backend/ docs/auth/builder/storefront/admin), known limitations, and explicit production/backend/demo readiness calls - including correcting an initial draft's unpushed-commit count (53, not 10, per git log origin/B2B..HEAD). New docs/NEXT_PHASE.md: work that can only start once a real backend exists (gateway swap-in, mock removal, dormant-auth activation, role enforcement, integration/E2E tests, perf profiling, monitoring, maintenance-mode UI). docs/PROJECT_INDEX.md (the stated entry point) updated to link the new doc set and stop pointing at the now-archived BACKEND_API.md/AUTH.md. docs/FRONTEND-ROADMAP.md's "Known open items" replaced with pointers to the new category-split docs instead of a duplicated mixed list. Not swept: a handful of low-traffic docs (architecture ADRs, FRONTEND.md, EDITOR.md, ARCHITECTURE.md, PROJECT-STRUCTURE.md, StaticPages.md, ADMIN.md) still reference the old BACKEND_API.md/ AUTH.md filenames - noted as a known gap in PROJECT_STATUS.md rather than touched blindly, since they're historical-context docs, not the navigation entry point.
2026-07-26 12:35:26 +04:00
- [ ] Serve real `GET /bootstrap` **content** (branding, theme, navigation, seo — not just the transport) — per Bootstrap contract (§6; archive/BACKEND_API.md §4). This is a P0 blocker.
- [ ] Populate `bootstrap.apiEndpoints.{website,builder,backoffice}` records so tenant-scoped paths resolve at runtime (§6; audit §24 — no path literals exist in client code).
- [ ] Confirm tenant resolution inputs (host/slug/code) match `TenantConfig` so `ApiConfigService.getBaseUrl()` resolves the right base (§2, §6).
- [ ] Adopt a consistent error envelope; the client maps failures to a `backend-unavailable` screen for admin auth — keep error bodies non-leaky (§5b; security guidance).
- [ ] Set the header contract: accept `X-Region`, `X-Language` (RU/EN/AM), `Currency` (default RUB), `WebSessionID` on marketplace requests (§3).
### Phase 2 — Read-heavy domains (mostly confirm; already client-LIVE)
- [ ] Stand up `GET /category` returning the `CategoryDto` shape `CategoryMapper` tolerates (§8.4 Example A; audit §8) — already LIVE client-side.
- [ ] Stand up `GET /items/:id`, `GET /category/:id`, `GET /items/randomitems`, `GET /searchitems` within the `normalizeItem` tolerance envelope (§8.4 Example B; audit §4, §7).
docs: final project closeout - classify TODO, backend spec, status Classified every TODO.md item into one of DONE/BACKEND/PRODUCT DECISION/FUTURE VERSION/BUG, verified against source, not against prior docs: - BACKEND items (bootstrap content, builder draft/publish, 6 admin CRUD domains, media pipeline) confirmed already covered by BACKEND_INTEGRATION.md; appended a mapping appendix rather than duplicating raw bullets. Fixed 22 stale internal BACKEND_API.md cross-references left over from before that file was archived. - PRODUCT DECISION items (dark mode, brand-color WCAG contrast, stars.component token gap, footer Contacts content, advanced analytics, payment providers) moved to new docs/PRODUCT_BACKLOG.md. - FUTURE VERSION items (Angular 22, bundle splitting, cart-modal composition cleanup, hero-spacing investigation) moved to new docs/FUTURE_FEATURES.md. - BUG: rewrote docs/KNOWN-ISSUES.md down to the one real, verified, currently-reproducible frontend bug (Ed25519 admin-auth error codes session-expired/invalid-signature are unreachable - toAuthErrorShape() never reads a body error code, only maps HTTP status, and no status ever produces those two codes - confirmed by reading auth.service.ts + auth-error.model.ts). Condensed the "Fixed" history instead of carrying full verbose repro text forward. - DONE items removed outright (dead-code deletion, dashboard false positive, RC-02 fixes, stale "dynamic-renderer unwired"/"178 missing keys" claims already disproven by source). docs/TODO.md rewritten to the exact "no blockers" template - nothing left qualifies as a release blocker. New docs/PROJECT_STATUS.md: honest per-area status (frontend/backend/ docs/auth/builder/storefront/admin), known limitations, and explicit production/backend/demo readiness calls - including correcting an initial draft's unpushed-commit count (53, not 10, per git log origin/B2B..HEAD). New docs/NEXT_PHASE.md: work that can only start once a real backend exists (gateway swap-in, mock removal, dormant-auth activation, role enforcement, integration/E2E tests, perf profiling, monitoring, maintenance-mode UI). docs/PROJECT_INDEX.md (the stated entry point) updated to link the new doc set and stop pointing at the now-archived BACKEND_API.md/AUTH.md. docs/FRONTEND-ROADMAP.md's "Known open items" replaced with pointers to the new category-split docs instead of a duplicated mixed list. Not swept: a handful of low-traffic docs (architecture ADRs, FRONTEND.md, EDITOR.md, ARCHITECTURE.md, PROJECT-STRUCTURE.md, StaticPages.md, ADMIN.md) still reference the old BACKEND_API.md/ AUTH.md filenames - noted as a known gap in PROJECT_STATUS.md rather than touched blindly, since they're historical-context docs, not the navigation entry point.
2026-07-26 12:35:26 +04:00
- [ ] Serve admin categories CRUD via the existing `AdminCategoriesApiGateway` contract: `loadCategories`, `loadCategory`, `create/update/delete/restore`, `isSlugTaken` — per Categories CRUD contract (archive/BACKEND_API.md §6.9). **Already wired client-side (DONE).**
- [ ] Serve `GET /api/backoffice/products` and `GET /api/backoffice/categories` (storefront cards) — `ApiBackofficeDataProvider` is LIVE (audit §9).
- [ ] Stand up `GET /regions``Region[]` (feeds the `X-Region` header; client falls back to 6 hardcoded regions) (audit §12).
### Phase 3 — Write-heavy customer domains
docs: final project closeout - classify TODO, backend spec, status Classified every TODO.md item into one of DONE/BACKEND/PRODUCT DECISION/FUTURE VERSION/BUG, verified against source, not against prior docs: - BACKEND items (bootstrap content, builder draft/publish, 6 admin CRUD domains, media pipeline) confirmed already covered by BACKEND_INTEGRATION.md; appended a mapping appendix rather than duplicating raw bullets. Fixed 22 stale internal BACKEND_API.md cross-references left over from before that file was archived. - PRODUCT DECISION items (dark mode, brand-color WCAG contrast, stars.component token gap, footer Contacts content, advanced analytics, payment providers) moved to new docs/PRODUCT_BACKLOG.md. - FUTURE VERSION items (Angular 22, bundle splitting, cart-modal composition cleanup, hero-spacing investigation) moved to new docs/FUTURE_FEATURES.md. - BUG: rewrote docs/KNOWN-ISSUES.md down to the one real, verified, currently-reproducible frontend bug (Ed25519 admin-auth error codes session-expired/invalid-signature are unreachable - toAuthErrorShape() never reads a body error code, only maps HTTP status, and no status ever produces those two codes - confirmed by reading auth.service.ts + auth-error.model.ts). Condensed the "Fixed" history instead of carrying full verbose repro text forward. - DONE items removed outright (dead-code deletion, dashboard false positive, RC-02 fixes, stale "dynamic-renderer unwired"/"178 missing keys" claims already disproven by source). docs/TODO.md rewritten to the exact "no blockers" template - nothing left qualifies as a release blocker. New docs/PROJECT_STATUS.md: honest per-area status (frontend/backend/ docs/auth/builder/storefront/admin), known limitations, and explicit production/backend/demo readiness calls - including correcting an initial draft's unpushed-commit count (53, not 10, per git log origin/B2B..HEAD). New docs/NEXT_PHASE.md: work that can only start once a real backend exists (gateway swap-in, mock removal, dormant-auth activation, role enforcement, integration/E2E tests, perf profiling, monitoring, maintenance-mode UI). docs/PROJECT_INDEX.md (the stated entry point) updated to link the new doc set and stop pointing at the now-archived BACKEND_API.md/AUTH.md. docs/FRONTEND-ROADMAP.md's "Known open items" replaced with pointers to the new category-split docs instead of a duplicated mixed list. Not swept: a handful of low-traffic docs (architecture ADRs, FRONTEND.md, EDITOR.md, ARCHITECTURE.md, PROJECT-STRUCTURE.md, StaticPages.md, ADMIN.md) still reference the old BACKEND_API.md/ AUTH.md filenames - noted as a known gap in PROJECT_STATUS.md rather than touched blindly, since they're historical-context docs, not the navigation entry point.
2026-07-26 12:35:26 +04:00
- [ ] Keep `POST /cart` (`CartPaymentRequest``QrCreateResponse`) and the frozen QR/card payment polling working unchanged (§10; payments frozen per archive/BACKEND_API.md §2.8).
- [ ] Implement `POST /orders` (`CreateOrderRequest``CreateOrderResponse`) — client call is LIVE, fire-and-forget after payment (§10; archive/BACKEND_API.md §16.9, marked DONE client-side).
- [ ] Implement `POST /purchase-email` (email receipt) (audit §4).
- [ ] Accept review/question writes `POST /items/:id/callback` and `POST /items/:id/questiion` (**preserve the `questiion` typo** — it matches the client literal) (§11; audit §4).
### Phase 4 — Admin domains (each needs the token seam added first — §8.5)
docs: final project closeout - classify TODO, backend spec, status Classified every TODO.md item into one of DONE/BACKEND/PRODUCT DECISION/FUTURE VERSION/BUG, verified against source, not against prior docs: - BACKEND items (bootstrap content, builder draft/publish, 6 admin CRUD domains, media pipeline) confirmed already covered by BACKEND_INTEGRATION.md; appended a mapping appendix rather than duplicating raw bullets. Fixed 22 stale internal BACKEND_API.md cross-references left over from before that file was archived. - PRODUCT DECISION items (dark mode, brand-color WCAG contrast, stars.component token gap, footer Contacts content, advanced analytics, payment providers) moved to new docs/PRODUCT_BACKLOG.md. - FUTURE VERSION items (Angular 22, bundle splitting, cart-modal composition cleanup, hero-spacing investigation) moved to new docs/FUTURE_FEATURES.md. - BUG: rewrote docs/KNOWN-ISSUES.md down to the one real, verified, currently-reproducible frontend bug (Ed25519 admin-auth error codes session-expired/invalid-signature are unreachable - toAuthErrorShape() never reads a body error code, only maps HTTP status, and no status ever produces those two codes - confirmed by reading auth.service.ts + auth-error.model.ts). Condensed the "Fixed" history instead of carrying full verbose repro text forward. - DONE items removed outright (dead-code deletion, dashboard false positive, RC-02 fixes, stale "dynamic-renderer unwired"/"178 missing keys" claims already disproven by source). docs/TODO.md rewritten to the exact "no blockers" template - nothing left qualifies as a release blocker. New docs/PROJECT_STATUS.md: honest per-area status (frontend/backend/ docs/auth/builder/storefront/admin), known limitations, and explicit production/backend/demo readiness calls - including correcting an initial draft's unpushed-commit count (53, not 10, per git log origin/B2B..HEAD). New docs/NEXT_PHASE.md: work that can only start once a real backend exists (gateway swap-in, mock removal, dormant-auth activation, role enforcement, integration/E2E tests, perf profiling, monitoring, maintenance-mode UI). docs/PROJECT_INDEX.md (the stated entry point) updated to link the new doc set and stop pointing at the now-archived BACKEND_API.md/AUTH.md. docs/FRONTEND-ROADMAP.md's "Known open items" replaced with pointers to the new category-split docs instead of a duplicated mixed list. Not swept: a handful of low-traffic docs (architecture ADRs, FRONTEND.md, EDITOR.md, ARCHITECTURE.md, PROJECT-STRUCTURE.md, StaticPages.md, ADMIN.md) still reference the old BACKEND_API.md/ AUTH.md filenames - noted as a known gap in PROJECT_STATUS.md rather than touched blindly, since they're historical-context docs, not the navigation entry point.
2026-07-26 12:35:26 +04:00
- [ ] Add `AdminOrdersGateway` token + `AdminOrdersApiGateway`, switch `AdminOrdersFacade` to the token; implement `GET/POST /backoffice/orders*` incl. `POST /backoffice/orders/:id/status` respecting the order state machine — per Orders CRUD contract (§8.4 Example C, §8.5; archive/BACKEND_API.md §6.11/§8.1).
- [ ] Add `AdminProductsGateway` token + `AdminProductsApiGateway`, switch `AdminProductsFacade`; implement Products CRUD + variants — per Products CRUD contract (§8.5; archive/BACKEND_API.md §6.10/§7.2).
- [ ] Add `AdminTransactionsGateway` token + api gateway, switch `AdminTransactionsFacade`; implement transactions list/detail + `retryFailed` + `setFraudFlag` (tied to orders) — per Transactions contract (§8.5; archive/BACKEND_API.md §6.12).
- [ ] Add `AdminUsersGateway` token + api gateway, switch `AdminUsersFacade`; implement users/roles/invitations/sessions/audit — per Users contract (§8.5; archive/BACKEND_API.md §6.13). Reconcile the duplicate `AdminRole` naming (audit §25 #3).
- [ ] Add `AdminModerationGateway` token + api gateway, switch `AdminModerationFacade`; implement review + report status transitions — per Moderation contract (§8.5; archive/BACKEND_API.md §6.14/§8.4/§8.5).
- [ ] Add `AdminMonitoringGateway` token + api gateway, switch `AdminMonitoringFacade`; implement events/queues/webhooks reads — per Monitoring contract (§8.5; archive/BACKEND_API.md §6.16).
- [ ] Wire `AdminDashboardMetricsApiGateway` to the existing `ADMIN_DASHBOARD_METRICS_GATEWAY` token; implement `loadMetrics` — per Dashboard contract (§8.5; archive/BACKEND_API.md §6.15).
- [ ] Resolve Customers: derive from the real Orders token (`AdminCustomersFacade`) or add a first-class customers source (§8.3).
docs: final project closeout - classify TODO, backend spec, status Classified every TODO.md item into one of DONE/BACKEND/PRODUCT DECISION/FUTURE VERSION/BUG, verified against source, not against prior docs: - BACKEND items (bootstrap content, builder draft/publish, 6 admin CRUD domains, media pipeline) confirmed already covered by BACKEND_INTEGRATION.md; appended a mapping appendix rather than duplicating raw bullets. Fixed 22 stale internal BACKEND_API.md cross-references left over from before that file was archived. - PRODUCT DECISION items (dark mode, brand-color WCAG contrast, stars.component token gap, footer Contacts content, advanced analytics, payment providers) moved to new docs/PRODUCT_BACKLOG.md. - FUTURE VERSION items (Angular 22, bundle splitting, cart-modal composition cleanup, hero-spacing investigation) moved to new docs/FUTURE_FEATURES.md. - BUG: rewrote docs/KNOWN-ISSUES.md down to the one real, verified, currently-reproducible frontend bug (Ed25519 admin-auth error codes session-expired/invalid-signature are unreachable - toAuthErrorShape() never reads a body error code, only maps HTTP status, and no status ever produces those two codes - confirmed by reading auth.service.ts + auth-error.model.ts). Condensed the "Fixed" history instead of carrying full verbose repro text forward. - DONE items removed outright (dead-code deletion, dashboard false positive, RC-02 fixes, stale "dynamic-renderer unwired"/"178 missing keys" claims already disproven by source). docs/TODO.md rewritten to the exact "no blockers" template - nothing left qualifies as a release blocker. New docs/PROJECT_STATUS.md: honest per-area status (frontend/backend/ docs/auth/builder/storefront/admin), known limitations, and explicit production/backend/demo readiness calls - including correcting an initial draft's unpushed-commit count (53, not 10, per git log origin/B2B..HEAD). New docs/NEXT_PHASE.md: work that can only start once a real backend exists (gateway swap-in, mock removal, dormant-auth activation, role enforcement, integration/E2E tests, perf profiling, monitoring, maintenance-mode UI). docs/PROJECT_INDEX.md (the stated entry point) updated to link the new doc set and stop pointing at the now-archived BACKEND_API.md/AUTH.md. docs/FRONTEND-ROADMAP.md's "Known open items" replaced with pointers to the new category-split docs instead of a duplicated mixed list. Not swept: a handful of low-traffic docs (architecture ADRs, FRONTEND.md, EDITOR.md, ARCHITECTURE.md, PROJECT-STRUCTURE.md, StaticPages.md, ADMIN.md) still reference the old BACKEND_API.md/ AUTH.md filenames - noted as a known gap in PROJECT_STATUS.md rather than touched blindly, since they're historical-context docs, not the navigation entry point.
2026-07-26 12:35:26 +04:00
- [ ] Defer Analytics until orders/products/moderation are real and a tracking pipeline exists; then implement the analytics summary source (§8.3, §8.6 step 10; archive/BACKEND_API.md §6.17).
### Phase 5 — Builder / CMS (net-new write paths — no client call exists today)
docs: final project closeout - classify TODO, backend spec, status Classified every TODO.md item into one of DONE/BACKEND/PRODUCT DECISION/FUTURE VERSION/BUG, verified against source, not against prior docs: - BACKEND items (bootstrap content, builder draft/publish, 6 admin CRUD domains, media pipeline) confirmed already covered by BACKEND_INTEGRATION.md; appended a mapping appendix rather than duplicating raw bullets. Fixed 22 stale internal BACKEND_API.md cross-references left over from before that file was archived. - PRODUCT DECISION items (dark mode, brand-color WCAG contrast, stars.component token gap, footer Contacts content, advanced analytics, payment providers) moved to new docs/PRODUCT_BACKLOG.md. - FUTURE VERSION items (Angular 22, bundle splitting, cart-modal composition cleanup, hero-spacing investigation) moved to new docs/FUTURE_FEATURES.md. - BUG: rewrote docs/KNOWN-ISSUES.md down to the one real, verified, currently-reproducible frontend bug (Ed25519 admin-auth error codes session-expired/invalid-signature are unreachable - toAuthErrorShape() never reads a body error code, only maps HTTP status, and no status ever produces those two codes - confirmed by reading auth.service.ts + auth-error.model.ts). Condensed the "Fixed" history instead of carrying full verbose repro text forward. - DONE items removed outright (dead-code deletion, dashboard false positive, RC-02 fixes, stale "dynamic-renderer unwired"/"178 missing keys" claims already disproven by source). docs/TODO.md rewritten to the exact "no blockers" template - nothing left qualifies as a release blocker. New docs/PROJECT_STATUS.md: honest per-area status (frontend/backend/ docs/auth/builder/storefront/admin), known limitations, and explicit production/backend/demo readiness calls - including correcting an initial draft's unpushed-commit count (53, not 10, per git log origin/B2B..HEAD). New docs/NEXT_PHASE.md: work that can only start once a real backend exists (gateway swap-in, mock removal, dormant-auth activation, role enforcement, integration/E2E tests, perf profiling, monitoring, maintenance-mode UI). docs/PROJECT_INDEX.md (the stated entry point) updated to link the new doc set and stop pointing at the now-archived BACKEND_API.md/AUTH.md. docs/FRONTEND-ROADMAP.md's "Known open items" replaced with pointers to the new category-split docs instead of a duplicated mixed list. Not swept: a handful of low-traffic docs (architecture ADRs, FRONTEND.md, EDITOR.md, ARCHITECTURE.md, PROJECT-STRUCTURE.md, StaticPages.md, ADMIN.md) still reference the old BACKEND_API.md/ AUTH.md filenames - noted as a known gap in PROJECT_STATUS.md rather than touched blindly, since they're historical-context docs, not the navigation entry point.
2026-07-26 12:35:26 +04:00
- [ ] Implement builder bootstrap draft/publish/validate: `GET/PUT /builder/bootstrap/draft`, `POST /builder/bootstrap/publish`, `POST /builder/bootstrap/validate` — and add the client-side write call in `ProjectEditorFacade`/`ProjectEditorIoService` (§8.3, §17; archive/BACKEND_API.md §6.7). P0 for the builder.
- [ ] Implement content pages / CMS write path and wire `ContentManagementFacade` beyond in-memory bootstrap (§8.3, §16; archive/BACKEND_API.md §6.8).
- [ ] Implement the media upload/delete/replace pipeline behind `ApiMediaRepository` bound to the `MediaRepository` token (§8.5; archive/BACKEND_API.md §6.18/§10).
### Phase 6 — Hardening
- [ ] Add rate limiting on all write endpoints (orders, reviews, admin CRUD, media upload) (security guidance).
- [ ] Set CSP / security headers for the frontend origin (frontend + API share a domain by decision — remaining-work "Explicitly not in this list").
- [ ] Add audit logging for admin mutations (order status, role changes, moderation actions, publish) — the client already models `*AuditEntry` / timeline shapes (audit §23).
- [ ] Implement maintenance-mode / graceful `backend-unavailable` responses the client can surface (§5b).
- [ ] Implement `GET /items/batch?ids=` to unblock the user-experience id-only sync redesign (remaining-work #16; §8.3).
docs: final project closeout - classify TODO, backend spec, status Classified every TODO.md item into one of DONE/BACKEND/PRODUCT DECISION/FUTURE VERSION/BUG, verified against source, not against prior docs: - BACKEND items (bootstrap content, builder draft/publish, 6 admin CRUD domains, media pipeline) confirmed already covered by BACKEND_INTEGRATION.md; appended a mapping appendix rather than duplicating raw bullets. Fixed 22 stale internal BACKEND_API.md cross-references left over from before that file was archived. - PRODUCT DECISION items (dark mode, brand-color WCAG contrast, stars.component token gap, footer Contacts content, advanced analytics, payment providers) moved to new docs/PRODUCT_BACKLOG.md. - FUTURE VERSION items (Angular 22, bundle splitting, cart-modal composition cleanup, hero-spacing investigation) moved to new docs/FUTURE_FEATURES.md. - BUG: rewrote docs/KNOWN-ISSUES.md down to the one real, verified, currently-reproducible frontend bug (Ed25519 admin-auth error codes session-expired/invalid-signature are unreachable - toAuthErrorShape() never reads a body error code, only maps HTTP status, and no status ever produces those two codes - confirmed by reading auth.service.ts + auth-error.model.ts). Condensed the "Fixed" history instead of carrying full verbose repro text forward. - DONE items removed outright (dead-code deletion, dashboard false positive, RC-02 fixes, stale "dynamic-renderer unwired"/"178 missing keys" claims already disproven by source). docs/TODO.md rewritten to the exact "no blockers" template - nothing left qualifies as a release blocker. New docs/PROJECT_STATUS.md: honest per-area status (frontend/backend/ docs/auth/builder/storefront/admin), known limitations, and explicit production/backend/demo readiness calls - including correcting an initial draft's unpushed-commit count (53, not 10, per git log origin/B2B..HEAD). New docs/NEXT_PHASE.md: work that can only start once a real backend exists (gateway swap-in, mock removal, dormant-auth activation, role enforcement, integration/E2E tests, perf profiling, monitoring, maintenance-mode UI). docs/PROJECT_INDEX.md (the stated entry point) updated to link the new doc set and stop pointing at the now-archived BACKEND_API.md/AUTH.md. docs/FRONTEND-ROADMAP.md's "Known open items" replaced with pointers to the new category-split docs instead of a duplicated mixed list. Not swept: a handful of low-traffic docs (architecture ADRs, FRONTEND.md, EDITOR.md, ARCHITECTURE.md, PROJECT-STRUCTURE.md, StaticPages.md, ADMIN.md) still reference the old BACKEND_API.md/ AUTH.md filenames - noted as a known gap in PROJECT_STATUS.md rather than touched blindly, since they're historical-context docs, not the navigation entry point.
2026-07-26 12:35:26 +04:00
- [ ] Add search suggestions/catalog-filter source if pursuing #15 (§8.6 step 12; archive/BACKEND_API.md §6.6).
- [ ] Plan dynamic sitemap generation (server-side, no frontend action) (remaining-work #18).
docs: final project closeout - classify TODO, backend spec, status Classified every TODO.md item into one of DONE/BACKEND/PRODUCT DECISION/FUTURE VERSION/BUG, verified against source, not against prior docs: - BACKEND items (bootstrap content, builder draft/publish, 6 admin CRUD domains, media pipeline) confirmed already covered by BACKEND_INTEGRATION.md; appended a mapping appendix rather than duplicating raw bullets. Fixed 22 stale internal BACKEND_API.md cross-references left over from before that file was archived. - PRODUCT DECISION items (dark mode, brand-color WCAG contrast, stars.component token gap, footer Contacts content, advanced analytics, payment providers) moved to new docs/PRODUCT_BACKLOG.md. - FUTURE VERSION items (Angular 22, bundle splitting, cart-modal composition cleanup, hero-spacing investigation) moved to new docs/FUTURE_FEATURES.md. - BUG: rewrote docs/KNOWN-ISSUES.md down to the one real, verified, currently-reproducible frontend bug (Ed25519 admin-auth error codes session-expired/invalid-signature are unreachable - toAuthErrorShape() never reads a body error code, only maps HTTP status, and no status ever produces those two codes - confirmed by reading auth.service.ts + auth-error.model.ts). Condensed the "Fixed" history instead of carrying full verbose repro text forward. - DONE items removed outright (dead-code deletion, dashboard false positive, RC-02 fixes, stale "dynamic-renderer unwired"/"178 missing keys" claims already disproven by source). docs/TODO.md rewritten to the exact "no blockers" template - nothing left qualifies as a release blocker. New docs/PROJECT_STATUS.md: honest per-area status (frontend/backend/ docs/auth/builder/storefront/admin), known limitations, and explicit production/backend/demo readiness calls - including correcting an initial draft's unpushed-commit count (53, not 10, per git log origin/B2B..HEAD). New docs/NEXT_PHASE.md: work that can only start once a real backend exists (gateway swap-in, mock removal, dormant-auth activation, role enforcement, integration/E2E tests, perf profiling, monitoring, maintenance-mode UI). docs/PROJECT_INDEX.md (the stated entry point) updated to link the new doc set and stop pointing at the now-archived BACKEND_API.md/AUTH.md. docs/FRONTEND-ROADMAP.md's "Known open items" replaced with pointers to the new category-split docs instead of a duplicated mixed list. Not swept: a handful of low-traffic docs (architecture ADRs, FRONTEND.md, EDITOR.md, ARCHITECTURE.md, PROJECT-STRUCTURE.md, StaticPages.md, ADMIN.md) still reference the old BACKEND_API.md/ AUTH.md filenames - noted as a known gap in PROJECT_STATUS.md rather than touched blindly, since they're historical-context docs, not the navigation entry point.
2026-07-26 12:35:26 +04:00
---
docs: final documentation consolidation - one canonical doc set Audited every *.md in docs/ and root. Merged five overlapping backend docs (BACKEND_INTEGRATION.md + AUTHENTICATION.md + ERROR_CONTRACT.md + MAINTENANCE_MODE.md + the already-archived BACKEND_API.md/ BACKEND_API_REMAINING_WORK.md) into one canonical docs/BACKEND.md (4775 lines, 10 numbered sections) - deleted the four standalone files outright now that their content is fully inlined. Archived (not deleted - real historical value): ADMIN.md (Sprint 19-28 build log, sprint-report-shaped, not a living reference) and FRONTEND-ROADMAP.md (despite its name, a shipped-history changelog with detail no other doc has - not a forward roadmap, so keeping it in root alongside NEXT_PHASE.md was exactly the "10 roadmaps" confusion being cleaned up). Deleted outright (zero value): SPRINTS.md - a leftover copy-pasted sprint-kickoff prompt saved as a file, not documentation. Rewrote docs/PROJECT_STATUS.md with completion-percentage estimates per area (frontend/backend/UI/admin/storefront) and an explicit first-customer-readiness call. Rewrote docs/NEXT_PHASE.md to the strict 5-phase structure (backend integration -> production testing -> performance -> monitoring -> v2 ideas), pointing to PRODUCT_BACKLOG .md/FUTURE_FEATURES.md for phase 5 detail instead of duplicating it. Rewrote root README.md - was stale (referenced deleted pages/info, pages/legal folders from a prior RC pass), now covers architecture, frontend/backend status, how to run, mock<->API switch mechanism (useMockData in environment.ts), current folder structure, and a documentation map. Updated docs/PROJECT_INDEX.md (the stated entry point) to link only the surviving doc set - every remaining document is reachable from it. Fixed every broken/stale cross-reference to the deleted/renamed backend docs across ARCHITECTURE.md, EDITOR.md, FRONTEND.md, PROJECT-STRUCTURE.md, StaticPages.md, KNOWN-ISSUES.md (10 individual link fixes, verified by repo-wide grep before and after). Left CHANGELOG.md's two historical entries untouched - changelogs are append-only history, not live navigation, editing past entries would misrepresent what was true at the time. Not touched (explicitly out of scope): docs/architecture/foundation/** (enforced ADRs/governance, permanent not sprint-shaped), docs/context/** (Barry Cache infrastructure, "do not edit by hand" per CLAUDE.md), .claude/worktrees/** (separate git worktrees containing an unrelated project's docs, not this repo's documentation). docs/ root: 22 files -> 16. Plus 5 in docs/archive/ (was 3).
2026-07-26 14:56:25 +04:00
---
## 10. Maintenance Mode
Frontend contract for backend maintenance/availability signals: global, per-tenant,
per-module, read-only, scheduled, and single-feature-disable scenarios. Written from
the current source tree (branch `B2B`) — see `docs/context/BACKEND-AUDIT.md` for the
full backend surface this builds on.
**Existing frontend handling today: none.** There is no maintenance concept anywhere
in the frontend — no model field, no interceptor branch, no route, no component. This
document proposes a contract and marks every open question explicitly as either
"Requires backend decision" (the backend hasn't decided the signal shape) or "No
frontend UI currently exists for this - requires a future frontend task" (the signal
is plausible but no UI has been built to react to it).
The one adjacent, already-built pattern worth reusing is `AuthErrorPageComponent`
(`src/app/core/auth/pages/auth-error-page.component.ts`): a single component keyed by
an error-code route param, rendering `EmptyStateComponent` +
`ButtonComponent`, with a `Record<Code, {title, description, actionLabel}>` copy table
and a `retry()` handler. Section 7 proposes the maintenance screens follow this exact
shape rather than inventing a new one.
---
### 1. Global maintenance
Whole platform down for all tenants.
**What the backend should send:** `503 Service Unavailable` on every endpoint
(including `GET /bootstrap`), with a `Retry-After` header (seconds) and a structured
JSON body (see §7 for exact shape). `GET /bootstrap` is the critical path — it is the
first call the frontend makes (`ApiBootstrapProvider.loadBootstrap()`,
`src/app/core/bootstrap/providers/api-bootstrap.provider.ts`, `GET /bootstrap`) and
every facade that renders anything (`UiRuntimeFacade`, `WebsiteRuntimeFacade`,
`ProjectEditorFacade`, `ContentManagementFacade`, `DiagnosticsFacade`) depends on it
resolving.
**What the frontend currently does:** nothing maintenance-specific. Tracing the call
chain in `src/app/core/config/config.service.ts`:
```ts
this.bootstrap$ = this.provider.loadBootstrap().pipe(
tap(config => { this.bootstrapSnapshot = config; ... }),
shareReplay(1),
catchError(error => {
this.bootstrap$ = undefined;
this.bootstrapSnapshot = null;
return throwError(() => error);
})
);
```
Any bootstrap failure (503 or otherwise) just rethrows. Every one of the ~14 call
sites of `configService.loadBootstrap()` (footer, theme engine, branding engine,
platform-runtime, page-resolver, static-page-resolver, footer-resolver, diagnostics,
etc. — see `Grep` results for `loadBootstrap()` across `src/app`) either does not
subscribe to the error channel at all, or handles it locally and inconsistently.
There is no global "the whole app is down" screen.
**No frontend UI currently exists for this — requires a future frontend task.** A
clean contract would intercept a `503` on the bootstrap call specifically (distinct
from a 503 on a leaf endpoint, which should degrade that one section instead — see
§3) and route to a full-page takeover, structurally identical to
`AuthErrorPageComponent`: a `maintenance-page.component.ts` using
`EmptyStateComponent` + `ButtonComponent`, keyed off the response body's `reason`
(§7), with a retry button that calls `configService.loadBootstrap(true)`.
**Requires backend decision:** whether maintenance state is signaled by response
status alone (`503` on `/bootstrap`) or also via a dedicated
`GET /status` / `GET /maintenance` probe the frontend could poll while showing the
takeover screen, to auto-recover without the user manually retrying.
---
### 2. Per-tenant maintenance
Single tenant disabled while others operate normally.
This ties directly into tenant resolution: `TenantResolverService`
(`src/app/core/config/tenant-resolver.service.ts`) determines the tenant key before
`ApiConfigService.getBaseUrl()` resolves which base URL to call (`tenantApiBaseUrls`
map, or `tenantApiTemplate` with `{tenant}` substituted — see
`docs/context/BACKEND-AUDIT.md` §2). Because tenant resolution happens client-side
before any network call, a per-tenant maintenance signal can only surface through the
response to that tenant's own `GET /bootstrap` call — there is no separate
"is this tenant up" check today.
**What the backend should send:** the *same* `503` + structured body as global
maintenance (§7) on that tenant's `/bootstrap` response. The frontend has no way to
distinguish "this tenant is down" from "the whole platform is down" except by the
response body's content — so the body must carry enough to tell (e.g. a `scope` field:
`"global" | "tenant"`).
**What the frontend currently does:** nothing. `ConfigService.loadBootstrap()` is
tenant-agnostic from the frontend's point of view — it just calls whatever base URL
`ApiConfigService` resolved and doesn't know if a 503 means "this tenant" vs.
"everything."
**Requires backend decision:** the `scope` discriminator mentioned above, and whether
a disabled tenant's static/marketing content (branding, footer) should still resolve
from a cached/last-known bootstrap so the takeover page can show the tenant's own
logo, or whether it's a fully generic (unbranded) page. Given
`BootstrapConfig.branding`/`theme` are only available *after* a successful bootstrap
load, a tenant-branded maintenance page is not achievable without a design decision
here (e.g. serving branding via a separate lightweight endpoint that stays up even
when the tenant is otherwise disabled).
**No frontend UI currently exists for this — requires a future frontend task.** Same
takeover component as §1 can likely serve both scopes once the backend supplies
`scope`, but nothing renders differently for tenant-vs-global today because nothing
renders a maintenance screen at all yet.
---
### 3. Per-module maintenance
E.g. payments down but catalog still browsable.
**Existing granularity concept:** `BootstrapConfig.featureFlags`
(`FeatureFlagsConfig`, `src/app/shared/models/config/feature-flags.model.ts`) —
a flat `Record<string, boolean>` with known keys `wishlist, compare, reviews,
questions, comments, recommendations, blog, chat, analytics, notifications, coupons,
loyalty, giftCards, invoices` and an index signature for tenant-specific extras. This
is a **static, bootstrap-time** on/off switch per feature — not a live "is this
service currently degraded" signal, and it has no `payments` or `catalog` key today.
It's read once at bootstrap load and doesn't change until the next bootstrap refresh.
There is no separate "module health" concept distinct from `featureFlags`. The admin
dashboard's `healthChecks()` / `homeHealthChecks()` (`AdminDashboardFacade`,
`src/app/features/admin/dashboard/facade/admin-dashboard.facade.ts`) are **not**
module-availability checks — they validate the *local bootstrap document itself*
(schema version present, no missing translations, no invalid colors/widget refs/
layouts, draft-exists, etc.), entirely client-side, with no backend health probe
behind any row except product/category counts (which reflect load success/failure of
`ProductFacade`/`CategoryFacade`, not an explicit "payments module is down" signal).
`AdminMonitoringPageComponent` reuses the same boolean-shaped `healthChecks()` — it is
not a live service-status board either.
**What the backend should send:** each domain-specific endpoint (e.g. `POST /cart`,
`POST /orders`, `{qrApiUrl}/qr`) should independently return `503` with the structured
body (§7) with `scope: "module"` and a `module` field (e.g. `"payments"`) when that
subsystem specifically is down, while unrelated endpoints (`GET /category`,
`GET /items/{id}`) keep responding normally. This requires no new bootstrap field —
it's a per-request response behavior, consistent with REST conventions (the resource
itself is unavailable, not the whole API).
**What the frontend currently does:** nothing differentiates a per-module outage from
any other request failure. `ApiService` (`src/app/services/api.service.ts`) has no
per-endpoint error branching for 503; a failed `createCartPayment()`/`createOrder()`
call surfaces through whatever generic error handling the checkout components already
docs: finalize BACKEND.md as a self-contained implementation prompt Goal: this document alone, pasted into a fresh Claude session, should be enough for a backend dev to implement against without getting stuck or inventing conventions the frontend doesn't actually need. Fixed a real bug: ~65 cross-references throughout the document pointed to docs/AUTHENTICATION.md, docs/ERROR_CONTRACT.md, docs/MAINTENANCE_MODE.md - three sibling docs that were deleted and fully merged into this document's own §4/§6/§10 during the earlier doc-consolidation pass, but the in-text references were never updated. A fresh agent following those links would hit dead ends repeatedly. Bulk-replaced with in- document section references; hand-fixed ~4 sentences that framed §6/ §10 as "sibling task, in progress" (stale - both are complete, this document's §6/§10 already are the settled contract, nothing to wait on). Added "Recommended default" to every item across the document's 5 consolidated "Requires backend decision" registers (§2.12 framework, §3.21 CRUD cross-cutting - 18 items, §4 §12 auth open items - 9 items, §6 error-model summary - 9 items, §10 maintenance-mode list - 5 items). Each default is derived from what the frontend already implies or standard REST/security convention - no invented APIs or business rules. ~6 items are explicitly flagged as real business/security decisions instead (order state-machine rules, Ed25519 cutover strategy, refresh-token reuse-detection posture) since those carry consequences no amount of frontend-code-reading can resolve. Added a "How to use this document" preamble up front: work in §9's dependency order not document order, apply recommended defaults and keep moving, only stop for the explicitly-flagged business/security items, don't invent beyond what's written or directly implied. Frontend side: confirmed nothing else is left. docs/TODO.md already has zero blockers; docs/KNOWN-ISSUES.md's one open item (unreachable Ed25519 error-code UI) is correctly left open and documented rather than faked closed - fixing it needs a real backend emitting real distinguishable error codes, which doesn't exist yet and can't be fabricated without inventing an API contract. No frontend code touched. No architecture changed. No APIs invented.
2026-07-26 16:10:33 +04:00
have for network failures (out of scope for this section — see §6 for the
general error-response shape).
docs: final documentation consolidation - one canonical doc set Audited every *.md in docs/ and root. Merged five overlapping backend docs (BACKEND_INTEGRATION.md + AUTHENTICATION.md + ERROR_CONTRACT.md + MAINTENANCE_MODE.md + the already-archived BACKEND_API.md/ BACKEND_API_REMAINING_WORK.md) into one canonical docs/BACKEND.md (4775 lines, 10 numbered sections) - deleted the four standalone files outright now that their content is fully inlined. Archived (not deleted - real historical value): ADMIN.md (Sprint 19-28 build log, sprint-report-shaped, not a living reference) and FRONTEND-ROADMAP.md (despite its name, a shipped-history changelog with detail no other doc has - not a forward roadmap, so keeping it in root alongside NEXT_PHASE.md was exactly the "10 roadmaps" confusion being cleaned up). Deleted outright (zero value): SPRINTS.md - a leftover copy-pasted sprint-kickoff prompt saved as a file, not documentation. Rewrote docs/PROJECT_STATUS.md with completion-percentage estimates per area (frontend/backend/UI/admin/storefront) and an explicit first-customer-readiness call. Rewrote docs/NEXT_PHASE.md to the strict 5-phase structure (backend integration -> production testing -> performance -> monitoring -> v2 ideas), pointing to PRODUCT_BACKLOG .md/FUTURE_FEATURES.md for phase 5 detail instead of duplicating it. Rewrote root README.md - was stale (referenced deleted pages/info, pages/legal folders from a prior RC pass), now covers architecture, frontend/backend status, how to run, mock<->API switch mechanism (useMockData in environment.ts), current folder structure, and a documentation map. Updated docs/PROJECT_INDEX.md (the stated entry point) to link only the surviving doc set - every remaining document is reachable from it. Fixed every broken/stale cross-reference to the deleted/renamed backend docs across ARCHITECTURE.md, EDITOR.md, FRONTEND.md, PROJECT-STRUCTURE.md, StaticPages.md, KNOWN-ISSUES.md (10 individual link fixes, verified by repo-wide grep before and after). Left CHANGELOG.md's two historical entries untouched - changelogs are append-only history, not live navigation, editing past entries would misrepresent what was true at the time. Not touched (explicitly out of scope): docs/architecture/foundation/** (enforced ADRs/governance, permanent not sprint-shaped), docs/context/** (Barry Cache infrastructure, "do not edit by hand" per CLAUDE.md), .claude/worktrees/** (separate git worktrees containing an unrelated project's docs, not this repo's documentation). docs/ root: 22 files -> 16. Plus 5 in docs/archive/ (was 3).
2026-07-26 14:56:25 +04:00
**No frontend UI currently exists for this — requires a future frontend task.** The
checkout flow would need a "payments unavailable" inline state (banner or disabled
submit + tooltip, per §7) distinct from a generic error toast, and catalog browsing
would need to keep working untouched — which it structurally already would, since
`ProductFacade`/`CategoryFacade` and the payment calls are fully independent code
paths today (no shared failure state). That independence is a real asset: a payments
outage cannot accidentally break catalog browsing given the current facade
separation, but no UI exists yet to *tell the user* payments specifically are down
rather than "something went wrong."
---
### 4. Read-only mode
Writes disabled, reads still work.
**Does the frontend already assume this is possible?** Partially, structurally, but
not deliberately. Cart state is `LOCAL-ONLY` (`CartService`,
`src/app/services/cart.service.ts`, signal-based, persisted to `localStorage` key
`marketplace_cart`) — adding items to cart, changing quantities, and browsing the cart
UI works entirely client-side with **no backend call at all** until checkout. The
only writes that hit a backend are at the checkout boundary: `POST /cart`
(`createCartPayment`), `POST /orders` (`createOrder`), `POST /purchase-email`, and the
QR/card payment polling. So today, if the backend rejected writes only, catalog
browsing, search, wishlist/compare (also `LOCAL-ONLY`,
`LocalUserExperienceRepository`), and cart-building would all continue working simply
because they never touch the backend — but reviews (`POST /items/{id}/callback`) and
questions (`POST /items/{id}/questiion`) are also writes and would fail the same as
checkout, since both are LIVE endpoints via `ProductDataProvider`.
There is no code today that *checks for* a read-only flag and proactively disables
write UI (e.g. graying out "Add to cart" or the checkout button ahead of time). A
write attempt would only be discovered to be blocked when the write call itself
fails.
**What the backend should send:** `503` (or `403`, see note below) with the
structured body (§7), `scope: "readonly"`, on write endpoints specifically —
`POST /cart`, `POST /orders`, `POST /purchase-email`, `POST /items/{id}/callback`,
`POST /items/{id}/questiion`, `POST /websession/{sessionId}` (cart sync) — while GET
endpoints keep working. `403 Forbidden` is arguably more correct REST semantics for
"this resource forbids this method during a maintenance window" than `503`, but `503`
+ `Retry-After` communicates "temporary" more clearly to a client and is
recommended so the frontend can offer a countdown/retry consistent with §5's pattern.
**Requires backend decision:** which status code is authoritative — this should be
docs: finalize BACKEND.md as a self-contained implementation prompt Goal: this document alone, pasted into a fresh Claude session, should be enough for a backend dev to implement against without getting stuck or inventing conventions the frontend doesn't actually need. Fixed a real bug: ~65 cross-references throughout the document pointed to docs/AUTHENTICATION.md, docs/ERROR_CONTRACT.md, docs/MAINTENANCE_MODE.md - three sibling docs that were deleted and fully merged into this document's own §4/§6/§10 during the earlier doc-consolidation pass, but the in-text references were never updated. A fresh agent following those links would hit dead ends repeatedly. Bulk-replaced with in- document section references; hand-fixed ~4 sentences that framed §6/ §10 as "sibling task, in progress" (stale - both are complete, this document's §6/§10 already are the settled contract, nothing to wait on). Added "Recommended default" to every item across the document's 5 consolidated "Requires backend decision" registers (§2.12 framework, §3.21 CRUD cross-cutting - 18 items, §4 §12 auth open items - 9 items, §6 error-model summary - 9 items, §10 maintenance-mode list - 5 items). Each default is derived from what the frontend already implies or standard REST/security convention - no invented APIs or business rules. ~6 items are explicitly flagged as real business/security decisions instead (order state-machine rules, Ed25519 cutover strategy, refresh-token reuse-detection posture) since those carry consequences no amount of frontend-code-reading can resolve. Added a "How to use this document" preamble up front: work in §9's dependency order not document order, apply recommended defaults and keep moving, only stop for the explicitly-flagged business/security items, don't invent beyond what's written or directly implied. Frontend side: confirmed nothing else is left. docs/TODO.md already has zero blockers; docs/KNOWN-ISSUES.md's one open item (unreachable Ed25519 error-code UI) is correctly left open and documented rather than faked closed - fixing it needs a real backend emitting real distinguishable error codes, which doesn't exist yet and can't be fabricated without inventing an API contract. No frontend code touched. No architecture changed. No APIs invented.
2026-07-26 16:10:33 +04:00
pinned down jointly with whatever `§6` settles on for its 5xx
docs: final documentation consolidation - one canonical doc set Audited every *.md in docs/ and root. Merged five overlapping backend docs (BACKEND_INTEGRATION.md + AUTHENTICATION.md + ERROR_CONTRACT.md + MAINTENANCE_MODE.md + the already-archived BACKEND_API.md/ BACKEND_API_REMAINING_WORK.md) into one canonical docs/BACKEND.md (4775 lines, 10 numbered sections) - deleted the four standalone files outright now that their content is fully inlined. Archived (not deleted - real historical value): ADMIN.md (Sprint 19-28 build log, sprint-report-shaped, not a living reference) and FRONTEND-ROADMAP.md (despite its name, a shipped-history changelog with detail no other doc has - not a forward roadmap, so keeping it in root alongside NEXT_PHASE.md was exactly the "10 roadmaps" confusion being cleaned up). Deleted outright (zero value): SPRINTS.md - a leftover copy-pasted sprint-kickoff prompt saved as a file, not documentation. Rewrote docs/PROJECT_STATUS.md with completion-percentage estimates per area (frontend/backend/UI/admin/storefront) and an explicit first-customer-readiness call. Rewrote docs/NEXT_PHASE.md to the strict 5-phase structure (backend integration -> production testing -> performance -> monitoring -> v2 ideas), pointing to PRODUCT_BACKLOG .md/FUTURE_FEATURES.md for phase 5 detail instead of duplicating it. Rewrote root README.md - was stale (referenced deleted pages/info, pages/legal folders from a prior RC pass), now covers architecture, frontend/backend status, how to run, mock<->API switch mechanism (useMockData in environment.ts), current folder structure, and a documentation map. Updated docs/PROJECT_INDEX.md (the stated entry point) to link only the surviving doc set - every remaining document is reachable from it. Fixed every broken/stale cross-reference to the deleted/renamed backend docs across ARCHITECTURE.md, EDITOR.md, FRONTEND.md, PROJECT-STRUCTURE.md, StaticPages.md, KNOWN-ISSUES.md (10 individual link fixes, verified by repo-wide grep before and after). Left CHANGELOG.md's two historical entries untouched - changelogs are append-only history, not live navigation, editing past entries would misrepresent what was true at the time. Not touched (explicitly out of scope): docs/architecture/foundation/** (enforced ADRs/governance, permanent not sprint-shaped), docs/context/** (Barry Cache infrastructure, "do not edit by hand" per CLAUDE.md), .claude/worktrees/** (separate git worktrees containing an unrelated project's docs, not this repo's documentation). docs/ root: 22 files -> 16. Plus 5 in docs/archive/ (was 3).
2026-07-26 14:56:25 +04:00
conventions, since read-only is really "a subset of write endpoints return
maintenance-503."
**No frontend UI currently exists for this — requires a future frontend task.** No
bootstrap flag exists to proactively disable checkout/review/question submission
ahead of a failed request (e.g. `featureFlags.readOnly` or a dedicated
`platformStatus.readOnly` field would need to be added to `BootstrapConfig` if the
product wants a proactive banner instead of a reactive failure). Reactive handling
(showing an error when the write call 503s) can reuse the same inline
docs: finalize BACKEND.md as a self-contained implementation prompt Goal: this document alone, pasted into a fresh Claude session, should be enough for a backend dev to implement against without getting stuck or inventing conventions the frontend doesn't actually need. Fixed a real bug: ~65 cross-references throughout the document pointed to docs/AUTHENTICATION.md, docs/ERROR_CONTRACT.md, docs/MAINTENANCE_MODE.md - three sibling docs that were deleted and fully merged into this document's own §4/§6/§10 during the earlier doc-consolidation pass, but the in-text references were never updated. A fresh agent following those links would hit dead ends repeatedly. Bulk-replaced with in- document section references; hand-fixed ~4 sentences that framed §6/ §10 as "sibling task, in progress" (stale - both are complete, this document's §6/§10 already are the settled contract, nothing to wait on). Added "Recommended default" to every item across the document's 5 consolidated "Requires backend decision" registers (§2.12 framework, §3.21 CRUD cross-cutting - 18 items, §4 §12 auth open items - 9 items, §6 error-model summary - 9 items, §10 maintenance-mode list - 5 items). Each default is derived from what the frontend already implies or standard REST/security convention - no invented APIs or business rules. ~6 items are explicitly flagged as real business/security decisions instead (order state-machine rules, Ed25519 cutover strategy, refresh-token reuse-detection posture) since those carry consequences no amount of frontend-code-reading can resolve. Added a "How to use this document" preamble up front: work in §9's dependency order not document order, apply recommended defaults and keep moving, only stop for the explicitly-flagged business/security items, don't invent beyond what's written or directly implied. Frontend side: confirmed nothing else is left. docs/TODO.md already has zero blockers; docs/KNOWN-ISSUES.md's one open item (unreachable Ed25519 error-code UI) is correctly left open and documented rather than faked closed - fixing it needs a real backend emitting real distinguishable error codes, which doesn't exist yet and can't be fabricated without inventing an API contract. No frontend code touched. No architecture changed. No APIs invented.
2026-07-26 16:10:33 +04:00
error-state pattern as §3/§6 once `§6` defines the generic error body
docs: final documentation consolidation - one canonical doc set Audited every *.md in docs/ and root. Merged five overlapping backend docs (BACKEND_INTEGRATION.md + AUTHENTICATION.md + ERROR_CONTRACT.md + MAINTENANCE_MODE.md + the already-archived BACKEND_API.md/ BACKEND_API_REMAINING_WORK.md) into one canonical docs/BACKEND.md (4775 lines, 10 numbered sections) - deleted the four standalone files outright now that their content is fully inlined. Archived (not deleted - real historical value): ADMIN.md (Sprint 19-28 build log, sprint-report-shaped, not a living reference) and FRONTEND-ROADMAP.md (despite its name, a shipped-history changelog with detail no other doc has - not a forward roadmap, so keeping it in root alongside NEXT_PHASE.md was exactly the "10 roadmaps" confusion being cleaned up). Deleted outright (zero value): SPRINTS.md - a leftover copy-pasted sprint-kickoff prompt saved as a file, not documentation. Rewrote docs/PROJECT_STATUS.md with completion-percentage estimates per area (frontend/backend/UI/admin/storefront) and an explicit first-customer-readiness call. Rewrote docs/NEXT_PHASE.md to the strict 5-phase structure (backend integration -> production testing -> performance -> monitoring -> v2 ideas), pointing to PRODUCT_BACKLOG .md/FUTURE_FEATURES.md for phase 5 detail instead of duplicating it. Rewrote root README.md - was stale (referenced deleted pages/info, pages/legal folders from a prior RC pass), now covers architecture, frontend/backend status, how to run, mock<->API switch mechanism (useMockData in environment.ts), current folder structure, and a documentation map. Updated docs/PROJECT_INDEX.md (the stated entry point) to link only the surviving doc set - every remaining document is reachable from it. Fixed every broken/stale cross-reference to the deleted/renamed backend docs across ARCHITECTURE.md, EDITOR.md, FRONTEND.md, PROJECT-STRUCTURE.md, StaticPages.md, KNOWN-ISSUES.md (10 individual link fixes, verified by repo-wide grep before and after). Left CHANGELOG.md's two historical entries untouched - changelogs are append-only history, not live navigation, editing past entries would misrepresent what was true at the time. Not touched (explicitly out of scope): docs/architecture/foundation/** (enforced ADRs/governance, permanent not sprint-shaped), docs/context/** (Barry Cache infrastructure, "do not edit by hand" per CLAUDE.md), .claude/worktrees/** (separate git worktrees containing an unrelated project's docs, not this repo's documentation). docs/ root: 22 files -> 16. Plus 5 in docs/archive/ (was 3).
2026-07-26 14:56:25 +04:00
handling.
---
### 5. Scheduled maintenance
Advance notice pattern (banner / countdown) ahead of a maintenance window.
**What exists in the frontend today:** nothing. No banner component, no countdown
component, no bootstrap field for an upcoming maintenance window.
**Requires backend decision — proposed minimal contract:** add an optional field to
`BootstrapConfig` (loaded once per session/on refresh via `GET /bootstrap`), e.g.:
```ts
interface ScheduledMaintenanceNotice {
startsAt: string; // ISO 8601
endsAt?: string; // ISO 8601, optional if duration is unknown
scope: 'global' | 'tenant' | 'module';
module?: string; // present when scope === 'module'
messageKey?: string; // optional i18n key/translated string for custom copy
}
```
surfaced as `bootstrap.maintenanceNotice?: ScheduledMaintenanceNotice | null`. This
keeps the mechanism consistent with how the platform already declares other
runtime-configured, backend-authored state (feature flags, tenant config, API
endpoint records all live in the bootstrap document per
`docs/context/BACKEND-AUDIT.md` §6) rather than inventing a new polling endpoint. A
polling `GET /maintenance-notice` endpoint is an alternative if the notice needs to
appear/change without a full bootstrap refresh — that tradeoff is the backend
decision.
**No frontend UI currently exists for this — requires a future frontend task.** A
dismissible banner component reading `bootstrap.maintenanceNotice` and showing a
localized "maintenance starts in Xh Ym" countdown would need to be built and mounted
at a layout level (header or a global banner slot) — no such banner or countdown
component exists in `src/app/shared/ui/` today.
---
### 6. Temporary feature disable
Single feature toggled off without full maintenance — e.g. reviews temporarily
disabled while the rest of the product page works.
**This is the one scenario the frontend already has a real mechanism for**, via
`BootstrapConfig.featureFlags` (§3). Setting `featureFlags.reviews = false` in the
bootstrap document is exactly the existing, live mechanism for "reviews are off right
now" — it's read by whatever consumes `FeatureConfigService`
(`src/app/core/config/*`) and gates the relevant UI. This is a **deploy/config-time**
toggle (changes on next bootstrap load), not a live incident-response toggle, but
structurally it is the same shape a backend team would use to kill a misbehaving
feature quickly: update the bootstrap document (or whatever backend-side config
drives it), and the next bootstrap fetch picks it up.
**Recommendation:** reuse `featureFlags` for this scenario rather than introducing a
parallel mechanism — it already exists, is already wired through to the UI in the
relevant places, and matches the "temporary, single-feature, not a full outage"
framing exactly. No backend decision needed for the *mechanism*; only for *process*
(how fast a flag flip propagates — depends on bootstrap cache/refresh cadence, which
is outside this doc's scope).
**Gap:** `featureFlags` has no `payments` or `catalog` key and is a boolean only — it
can't express "reviews disabled with reason X, back at time Y" the way §5's proposed
`maintenanceNotice` can. If product wants a "reviews are temporarily unavailable —
back tomorrow" message rather than the feature silently disappearing, that needs the
richer shape from §5, scoped to `module`, not a plain `featureFlags` boolean.
---
### 7. Recommended API responses
All maintenance-scenario responses use HTTP `503 Service Unavailable` (except the
read-only debate in §4) with a `Retry-After` header (seconds, standard HTTP) and a
docs: finalize BACKEND.md as a self-contained implementation prompt Goal: this document alone, pasted into a fresh Claude session, should be enough for a backend dev to implement against without getting stuck or inventing conventions the frontend doesn't actually need. Fixed a real bug: ~65 cross-references throughout the document pointed to docs/AUTHENTICATION.md, docs/ERROR_CONTRACT.md, docs/MAINTENANCE_MODE.md - three sibling docs that were deleted and fully merged into this document's own §4/§6/§10 during the earlier doc-consolidation pass, but the in-text references were never updated. A fresh agent following those links would hit dead ends repeatedly. Bulk-replaced with in- document section references; hand-fixed ~4 sentences that framed §6/ §10 as "sibling task, in progress" (stale - both are complete, this document's §6/§10 already are the settled contract, nothing to wait on). Added "Recommended default" to every item across the document's 5 consolidated "Requires backend decision" registers (§2.12 framework, §3.21 CRUD cross-cutting - 18 items, §4 §12 auth open items - 9 items, §6 error-model summary - 9 items, §10 maintenance-mode list - 5 items). Each default is derived from what the frontend already implies or standard REST/security convention - no invented APIs or business rules. ~6 items are explicitly flagged as real business/security decisions instead (order state-machine rules, Ed25519 cutover strategy, refresh-token reuse-detection posture) since those carry consequences no amount of frontend-code-reading can resolve. Added a "How to use this document" preamble up front: work in §9's dependency order not document order, apply recommended defaults and keep moving, only stop for the explicitly-flagged business/security items, don't invent beyond what's written or directly implied. Frontend side: confirmed nothing else is left. docs/TODO.md already has zero blockers; docs/KNOWN-ISSUES.md's one open item (unreachable Ed25519 error-code UI) is correctly left open and documented rather than faked closed - fixing it needs a real backend emitting real distinguishable error codes, which doesn't exist yet and can't be fabricated without inventing an API contract. No frontend code touched. No architecture changed. No APIs invented.
2026-07-26 16:10:33 +04:00
JSON body, nested under the §6 error envelope (`{ error: { code, message, ... } }`)
rather than as a competing top-level shape. The fields below are what the frontend needs regardless
docs: final documentation consolidation - one canonical doc set Audited every *.md in docs/ and root. Merged five overlapping backend docs (BACKEND_INTEGRATION.md + AUTHENTICATION.md + ERROR_CONTRACT.md + MAINTENANCE_MODE.md + the already-archived BACKEND_API.md/ BACKEND_API_REMAINING_WORK.md) into one canonical docs/BACKEND.md (4775 lines, 10 numbered sections) - deleted the four standalone files outright now that their content is fully inlined. Archived (not deleted - real historical value): ADMIN.md (Sprint 19-28 build log, sprint-report-shaped, not a living reference) and FRONTEND-ROADMAP.md (despite its name, a shipped-history changelog with detail no other doc has - not a forward roadmap, so keeping it in root alongside NEXT_PHASE.md was exactly the "10 roadmaps" confusion being cleaned up). Deleted outright (zero value): SPRINTS.md - a leftover copy-pasted sprint-kickoff prompt saved as a file, not documentation. Rewrote docs/PROJECT_STATUS.md with completion-percentage estimates per area (frontend/backend/UI/admin/storefront) and an explicit first-customer-readiness call. Rewrote docs/NEXT_PHASE.md to the strict 5-phase structure (backend integration -> production testing -> performance -> monitoring -> v2 ideas), pointing to PRODUCT_BACKLOG .md/FUTURE_FEATURES.md for phase 5 detail instead of duplicating it. Rewrote root README.md - was stale (referenced deleted pages/info, pages/legal folders from a prior RC pass), now covers architecture, frontend/backend status, how to run, mock<->API switch mechanism (useMockData in environment.ts), current folder structure, and a documentation map. Updated docs/PROJECT_INDEX.md (the stated entry point) to link only the surviving doc set - every remaining document is reachable from it. Fixed every broken/stale cross-reference to the deleted/renamed backend docs across ARCHITECTURE.md, EDITOR.md, FRONTEND.md, PROJECT-STRUCTURE.md, StaticPages.md, KNOWN-ISSUES.md (10 individual link fixes, verified by repo-wide grep before and after). Left CHANGELOG.md's two historical entries untouched - changelogs are append-only history, not live navigation, editing past entries would misrepresent what was true at the time. Not touched (explicitly out of scope): docs/architecture/foundation/** (enforced ADRs/governance, permanent not sprint-shaped), docs/context/** (Barry Cache infrastructure, "do not edit by hand" per CLAUDE.md), .claude/worktrees/** (separate git worktrees containing an unrelated project's docs, not this repo's documentation). docs/ root: 22 files -> 16. Plus 5 in docs/archive/ (was 3).
2026-07-26 14:56:25 +04:00
of the outer envelope:
```json
{
"status": 503,
"code": "maintenance",
"scope": "global",
"module": null,
"reason": "scheduled",
"message": "The marketplace is temporarily unavailable for scheduled maintenance.",
"retryAfter": 1800,
"startedAt": "2026-07-26T02:00:00Z",
"expectedEndAt": "2026-07-26T03:00:00Z"
}
```
Field notes:
- `scope`: `"global" | "tenant" | "module" | "readonly"` — lets the frontend pick the
right UI (full takeover vs. inline banner vs. disabled control) without guessing
from status code alone.
- `module`: present only when `scope === "module"` (e.g. `"payments"`, `"reviews"`).
- `reason`: `"scheduled" | "incident" | "disabled"` — free-form enough for the
frontend to choose copy tone (planned vs. unplanned) without needing new fields
per scenario.
- `retryAfter`: mirrors the `Retry-After` header in the body too, so a client that
only reads JSON (not headers) still gets it — useful since some HttpClient error
paths surface the body more readily than headers depending on interceptor
structure.
- `startedAt` / `expectedEndAt`: optional, ISO 8601, for countdown/banner copy (§5).
Per-scenario summary:
| Scenario | Status | `scope` | Notes |
|---|---|---|---|
| Global | 503 | `"global"` | On every endpoint, especially `/bootstrap` |
| Per-tenant | 503 | `"tenant"` | On that tenant's `/bootstrap` and all its endpoints |
| Per-module | 503 | `"module"` | Only on that module's endpoints (e.g. `/cart`, `/orders`) |
docs: finalize BACKEND.md as a self-contained implementation prompt Goal: this document alone, pasted into a fresh Claude session, should be enough for a backend dev to implement against without getting stuck or inventing conventions the frontend doesn't actually need. Fixed a real bug: ~65 cross-references throughout the document pointed to docs/AUTHENTICATION.md, docs/ERROR_CONTRACT.md, docs/MAINTENANCE_MODE.md - three sibling docs that were deleted and fully merged into this document's own §4/§6/§10 during the earlier doc-consolidation pass, but the in-text references were never updated. A fresh agent following those links would hit dead ends repeatedly. Bulk-replaced with in- document section references; hand-fixed ~4 sentences that framed §6/ §10 as "sibling task, in progress" (stale - both are complete, this document's §6/§10 already are the settled contract, nothing to wait on). Added "Recommended default" to every item across the document's 5 consolidated "Requires backend decision" registers (§2.12 framework, §3.21 CRUD cross-cutting - 18 items, §4 §12 auth open items - 9 items, §6 error-model summary - 9 items, §10 maintenance-mode list - 5 items). Each default is derived from what the frontend already implies or standard REST/security convention - no invented APIs or business rules. ~6 items are explicitly flagged as real business/security decisions instead (order state-machine rules, Ed25519 cutover strategy, refresh-token reuse-detection posture) since those carry consequences no amount of frontend-code-reading can resolve. Added a "How to use this document" preamble up front: work in §9's dependency order not document order, apply recommended defaults and keep moving, only stop for the explicitly-flagged business/security items, don't invent beyond what's written or directly implied. Frontend side: confirmed nothing else is left. docs/TODO.md already has zero blockers; docs/KNOWN-ISSUES.md's one open item (unreachable Ed25519 error-code UI) is correctly left open and documented rather than faked closed - fixing it needs a real backend emitting real distinguishable error codes, which doesn't exist yet and can't be fabricated without inventing an API contract. No frontend code touched. No architecture changed. No APIs invented.
2026-07-26 16:10:33 +04:00
| Read-only | 503 or 403 | `"readonly"` | Only on write endpoints; GETs unaffected — pin down with `§6` |
docs: final documentation consolidation - one canonical doc set Audited every *.md in docs/ and root. Merged five overlapping backend docs (BACKEND_INTEGRATION.md + AUTHENTICATION.md + ERROR_CONTRACT.md + MAINTENANCE_MODE.md + the already-archived BACKEND_API.md/ BACKEND_API_REMAINING_WORK.md) into one canonical docs/BACKEND.md (4775 lines, 10 numbered sections) - deleted the four standalone files outright now that their content is fully inlined. Archived (not deleted - real historical value): ADMIN.md (Sprint 19-28 build log, sprint-report-shaped, not a living reference) and FRONTEND-ROADMAP.md (despite its name, a shipped-history changelog with detail no other doc has - not a forward roadmap, so keeping it in root alongside NEXT_PHASE.md was exactly the "10 roadmaps" confusion being cleaned up). Deleted outright (zero value): SPRINTS.md - a leftover copy-pasted sprint-kickoff prompt saved as a file, not documentation. Rewrote docs/PROJECT_STATUS.md with completion-percentage estimates per area (frontend/backend/UI/admin/storefront) and an explicit first-customer-readiness call. Rewrote docs/NEXT_PHASE.md to the strict 5-phase structure (backend integration -> production testing -> performance -> monitoring -> v2 ideas), pointing to PRODUCT_BACKLOG .md/FUTURE_FEATURES.md for phase 5 detail instead of duplicating it. Rewrote root README.md - was stale (referenced deleted pages/info, pages/legal folders from a prior RC pass), now covers architecture, frontend/backend status, how to run, mock<->API switch mechanism (useMockData in environment.ts), current folder structure, and a documentation map. Updated docs/PROJECT_INDEX.md (the stated entry point) to link only the surviving doc set - every remaining document is reachable from it. Fixed every broken/stale cross-reference to the deleted/renamed backend docs across ARCHITECTURE.md, EDITOR.md, FRONTEND.md, PROJECT-STRUCTURE.md, StaticPages.md, KNOWN-ISSUES.md (10 individual link fixes, verified by repo-wide grep before and after). Left CHANGELOG.md's two historical entries untouched - changelogs are append-only history, not live navigation, editing past entries would misrepresent what was true at the time. Not touched (explicitly out of scope): docs/architecture/foundation/** (enforced ADRs/governance, permanent not sprint-shaped), docs/context/** (Barry Cache infrastructure, "do not edit by hand" per CLAUDE.md), .claude/worktrees/** (separate git worktrees containing an unrelated project's docs, not this repo's documentation). docs/ root: 22 files -> 16. Plus 5 in docs/archive/ (was 3).
2026-07-26 14:56:25 +04:00
| Scheduled (advance notice) | 200, via `bootstrap.maintenanceNotice` | n/a | Not an error response — a proactive field on the normal `/bootstrap` payload, see §5 |
| Temporary feature disable | 200, via `bootstrap.featureFlags.<key> = false` | n/a | Not an error response — existing bootstrap mechanism, see §6 |
---
### 8. Frontend behavior
Grounded in the UI patterns that already exist (`EmptyStateComponent`
(`src/app/shared/ui/empty-state/empty-state.component.ts`), the `errorTitle` /
`error` / `retry` i18n-key convention used across catalog, product details, and
generic list widgets (`src/app/i18n/en.ts`), and `AuthErrorPageComponent`'s
code-keyed full-page pattern). No new UI concepts are invented below beyond composing
these.
| Scenario | Recommended UI | Existing pattern reused | Status |
|---|---|---|---|
| Global maintenance | Full-page takeover, replaces the entire app shell (no header/footer, since branding may be unavailable — see §2) | `AuthErrorPageComponent` shape: `EmptyStateComponent` + `ButtonComponent`, code-keyed copy, `retry()` action | No frontend UI currently exists for this — requires a future frontend task |
| Per-tenant maintenance | Same full-page takeover as global, ideally tenant-branded if the backend decision in §2 allows branding to still resolve | Same as above | No frontend UI currently exists for this — requires a future frontend task |
| Per-module maintenance | Inline empty-state/banner scoped to the affected section only (e.g. checkout step shows `EmptyStateComponent` with `errorTitle`/`error`/`retry` copy; catalog pages untouched) | `EmptyStateComponent` + the `errorTitle`/`error`/`retry` i18n triple already used in `catalog`/`productDetails`/generic-list translations | No frontend UI currently exists for this — requires a future frontend task |
| Read-only mode | Disabled write control (e.g. "Add to cart" / "Submit review" button) + tooltip explaining why, OR a reactive error state on submit if no proactive flag exists (§4) | Disabled-button-plus-tooltip is a common pattern in the design system but not wired to any maintenance signal today | No frontend UI currently exists for this — requires a future frontend task |
| Scheduled maintenance | Dismissible banner at layout/header level with countdown copy | No banner/countdown component exists in `src/app/shared/ui/` today | No frontend UI currently exists for this — requires a future frontend task |
| Temporary feature disable | Feature's own UI simply doesn't render (existing `featureFlags` gating), optionally with a short "temporarily unavailable" note if `messageKey` (§5) is present | Existing `featureFlags` boolean gating (already live) | Existing mechanism works; richer messaging is the only gap |
---
### Summary: what's proposed/new vs. what already exists
**Already exists and can be reused as-is:**
- `BootstrapConfig.featureFlags` — static per-feature kill switch (§3, §6).
- `EmptyStateComponent` + `errorTitle`/`error`/`retry` i18n convention — the inline
error-state building block for any scenario.
- `AuthErrorPageComponent` — the full-page-takeover shape (code-keyed copy record,
`EmptyStateComponent` + `ButtonComponent`, `retry()` handler) to model a maintenance
page after.
- Cart's `LOCAL-ONLY` design already means most of "read-only browsing" works
incidentally, since browsing/cart-building never call the backend.
**Proposed/new (this document introduces):**
- The `scope`/`module`/`reason` structured 503 body (§7).
- `bootstrap.maintenanceNotice` (§5) for scheduled-maintenance advance notice.
- A dedicated `maintenance-page.component.ts` full-page takeover (§1/§2).
- Inline per-module/read-only error and disabled-control states wired to the new 503
shape (§3/§4).
---
### Requires backend decision (full list)
docs: finalize BACKEND.md as a self-contained implementation prompt Goal: this document alone, pasted into a fresh Claude session, should be enough for a backend dev to implement against without getting stuck or inventing conventions the frontend doesn't actually need. Fixed a real bug: ~65 cross-references throughout the document pointed to docs/AUTHENTICATION.md, docs/ERROR_CONTRACT.md, docs/MAINTENANCE_MODE.md - three sibling docs that were deleted and fully merged into this document's own §4/§6/§10 during the earlier doc-consolidation pass, but the in-text references were never updated. A fresh agent following those links would hit dead ends repeatedly. Bulk-replaced with in- document section references; hand-fixed ~4 sentences that framed §6/ §10 as "sibling task, in progress" (stale - both are complete, this document's §6/§10 already are the settled contract, nothing to wait on). Added "Recommended default" to every item across the document's 5 consolidated "Requires backend decision" registers (§2.12 framework, §3.21 CRUD cross-cutting - 18 items, §4 §12 auth open items - 9 items, §6 error-model summary - 9 items, §10 maintenance-mode list - 5 items). Each default is derived from what the frontend already implies or standard REST/security convention - no invented APIs or business rules. ~6 items are explicitly flagged as real business/security decisions instead (order state-machine rules, Ed25519 cutover strategy, refresh-token reuse-detection posture) since those carry consequences no amount of frontend-code-reading can resolve. Added a "How to use this document" preamble up front: work in §9's dependency order not document order, apply recommended defaults and keep moving, only stop for the explicitly-flagged business/security items, don't invent beyond what's written or directly implied. Frontend side: confirmed nothing else is left. docs/TODO.md already has zero blockers; docs/KNOWN-ISSUES.md's one open item (unreachable Ed25519 error-code UI) is correctly left open and documented rather than faked closed - fixing it needs a real backend emitting real distinguishable error codes, which doesn't exist yet and can't be fabricated without inventing an API contract. No frontend code touched. No architecture changed. No APIs invented.
2026-07-26 16:10:33 +04:00
Each has a **Recommended default** — apply it unless flagged otherwise.
docs: final documentation consolidation - one canonical doc set Audited every *.md in docs/ and root. Merged five overlapping backend docs (BACKEND_INTEGRATION.md + AUTHENTICATION.md + ERROR_CONTRACT.md + MAINTENANCE_MODE.md + the already-archived BACKEND_API.md/ BACKEND_API_REMAINING_WORK.md) into one canonical docs/BACKEND.md (4775 lines, 10 numbered sections) - deleted the four standalone files outright now that their content is fully inlined. Archived (not deleted - real historical value): ADMIN.md (Sprint 19-28 build log, sprint-report-shaped, not a living reference) and FRONTEND-ROADMAP.md (despite its name, a shipped-history changelog with detail no other doc has - not a forward roadmap, so keeping it in root alongside NEXT_PHASE.md was exactly the "10 roadmaps" confusion being cleaned up). Deleted outright (zero value): SPRINTS.md - a leftover copy-pasted sprint-kickoff prompt saved as a file, not documentation. Rewrote docs/PROJECT_STATUS.md with completion-percentage estimates per area (frontend/backend/UI/admin/storefront) and an explicit first-customer-readiness call. Rewrote docs/NEXT_PHASE.md to the strict 5-phase structure (backend integration -> production testing -> performance -> monitoring -> v2 ideas), pointing to PRODUCT_BACKLOG .md/FUTURE_FEATURES.md for phase 5 detail instead of duplicating it. Rewrote root README.md - was stale (referenced deleted pages/info, pages/legal folders from a prior RC pass), now covers architecture, frontend/backend status, how to run, mock<->API switch mechanism (useMockData in environment.ts), current folder structure, and a documentation map. Updated docs/PROJECT_INDEX.md (the stated entry point) to link only the surviving doc set - every remaining document is reachable from it. Fixed every broken/stale cross-reference to the deleted/renamed backend docs across ARCHITECTURE.md, EDITOR.md, FRONTEND.md, PROJECT-STRUCTURE.md, StaticPages.md, KNOWN-ISSUES.md (10 individual link fixes, verified by repo-wide grep before and after). Left CHANGELOG.md's two historical entries untouched - changelogs are append-only history, not live navigation, editing past entries would misrepresent what was true at the time. Not touched (explicitly out of scope): docs/architecture/foundation/** (enforced ADRs/governance, permanent not sprint-shaped), docs/context/** (Barry Cache infrastructure, "do not edit by hand" per CLAUDE.md), .claude/worktrees/** (separate git worktrees containing an unrelated project's docs, not this repo's documentation). docs/ root: 22 files -> 16. Plus 5 in docs/archive/ (was 3).
2026-07-26 14:56:25 +04:00
- §1: whether a dedicated `GET /status`/`GET /maintenance` probe should exist for
auto-recovery polling, beyond a plain 503 on `/bootstrap`.
docs: finalize BACKEND.md as a self-contained implementation prompt Goal: this document alone, pasted into a fresh Claude session, should be enough for a backend dev to implement against without getting stuck or inventing conventions the frontend doesn't actually need. Fixed a real bug: ~65 cross-references throughout the document pointed to docs/AUTHENTICATION.md, docs/ERROR_CONTRACT.md, docs/MAINTENANCE_MODE.md - three sibling docs that were deleted and fully merged into this document's own §4/§6/§10 during the earlier doc-consolidation pass, but the in-text references were never updated. A fresh agent following those links would hit dead ends repeatedly. Bulk-replaced with in- document section references; hand-fixed ~4 sentences that framed §6/ §10 as "sibling task, in progress" (stale - both are complete, this document's §6/§10 already are the settled contract, nothing to wait on). Added "Recommended default" to every item across the document's 5 consolidated "Requires backend decision" registers (§2.12 framework, §3.21 CRUD cross-cutting - 18 items, §4 §12 auth open items - 9 items, §6 error-model summary - 9 items, §10 maintenance-mode list - 5 items). Each default is derived from what the frontend already implies or standard REST/security convention - no invented APIs or business rules. ~6 items are explicitly flagged as real business/security decisions instead (order state-machine rules, Ed25519 cutover strategy, refresh-token reuse-detection posture) since those carry consequences no amount of frontend-code-reading can resolve. Added a "How to use this document" preamble up front: work in §9's dependency order not document order, apply recommended defaults and keep moving, only stop for the explicitly-flagged business/security items, don't invent beyond what's written or directly implied. Frontend side: confirmed nothing else is left. docs/TODO.md already has zero blockers; docs/KNOWN-ISSUES.md's one open item (unreachable Ed25519 error-code UI) is correctly left open and documented rather than faked closed - fixing it needs a real backend emitting real distinguishable error codes, which doesn't exist yet and can't be fabricated without inventing an API contract. No frontend code touched. No architecture changed. No APIs invented.
2026-07-26 16:10:33 +04:00
**Recommended default:** no dedicated probe — reuse `GET /bootstrap`'s
existing 503/200 as the recovery signal; a second endpoint duplicates it.
docs: final documentation consolidation - one canonical doc set Audited every *.md in docs/ and root. Merged five overlapping backend docs (BACKEND_INTEGRATION.md + AUTHENTICATION.md + ERROR_CONTRACT.md + MAINTENANCE_MODE.md + the already-archived BACKEND_API.md/ BACKEND_API_REMAINING_WORK.md) into one canonical docs/BACKEND.md (4775 lines, 10 numbered sections) - deleted the four standalone files outright now that their content is fully inlined. Archived (not deleted - real historical value): ADMIN.md (Sprint 19-28 build log, sprint-report-shaped, not a living reference) and FRONTEND-ROADMAP.md (despite its name, a shipped-history changelog with detail no other doc has - not a forward roadmap, so keeping it in root alongside NEXT_PHASE.md was exactly the "10 roadmaps" confusion being cleaned up). Deleted outright (zero value): SPRINTS.md - a leftover copy-pasted sprint-kickoff prompt saved as a file, not documentation. Rewrote docs/PROJECT_STATUS.md with completion-percentage estimates per area (frontend/backend/UI/admin/storefront) and an explicit first-customer-readiness call. Rewrote docs/NEXT_PHASE.md to the strict 5-phase structure (backend integration -> production testing -> performance -> monitoring -> v2 ideas), pointing to PRODUCT_BACKLOG .md/FUTURE_FEATURES.md for phase 5 detail instead of duplicating it. Rewrote root README.md - was stale (referenced deleted pages/info, pages/legal folders from a prior RC pass), now covers architecture, frontend/backend status, how to run, mock<->API switch mechanism (useMockData in environment.ts), current folder structure, and a documentation map. Updated docs/PROJECT_INDEX.md (the stated entry point) to link only the surviving doc set - every remaining document is reachable from it. Fixed every broken/stale cross-reference to the deleted/renamed backend docs across ARCHITECTURE.md, EDITOR.md, FRONTEND.md, PROJECT-STRUCTURE.md, StaticPages.md, KNOWN-ISSUES.md (10 individual link fixes, verified by repo-wide grep before and after). Left CHANGELOG.md's two historical entries untouched - changelogs are append-only history, not live navigation, editing past entries would misrepresent what was true at the time. Not touched (explicitly out of scope): docs/architecture/foundation/** (enforced ADRs/governance, permanent not sprint-shaped), docs/context/** (Barry Cache infrastructure, "do not edit by hand" per CLAUDE.md), .claude/worktrees/** (separate git worktrees containing an unrelated project's docs, not this repo's documentation). docs/ root: 22 files -> 16. Plus 5 in docs/archive/ (was 3).
2026-07-26 14:56:25 +04:00
- §2: the `scope` discriminator (`"global"` vs `"tenant"`) so the frontend can tell
the two apart from a single tenant's bootstrap response; and whether a
disabled tenant's branding can still resolve for a branded takeover page.
docs: finalize BACKEND.md as a self-contained implementation prompt Goal: this document alone, pasted into a fresh Claude session, should be enough for a backend dev to implement against without getting stuck or inventing conventions the frontend doesn't actually need. Fixed a real bug: ~65 cross-references throughout the document pointed to docs/AUTHENTICATION.md, docs/ERROR_CONTRACT.md, docs/MAINTENANCE_MODE.md - three sibling docs that were deleted and fully merged into this document's own §4/§6/§10 during the earlier doc-consolidation pass, but the in-text references were never updated. A fresh agent following those links would hit dead ends repeatedly. Bulk-replaced with in- document section references; hand-fixed ~4 sentences that framed §6/ §10 as "sibling task, in progress" (stale - both are complete, this document's §6/§10 already are the settled contract, nothing to wait on). Added "Recommended default" to every item across the document's 5 consolidated "Requires backend decision" registers (§2.12 framework, §3.21 CRUD cross-cutting - 18 items, §4 §12 auth open items - 9 items, §6 error-model summary - 9 items, §10 maintenance-mode list - 5 items). Each default is derived from what the frontend already implies or standard REST/security convention - no invented APIs or business rules. ~6 items are explicitly flagged as real business/security decisions instead (order state-machine rules, Ed25519 cutover strategy, refresh-token reuse-detection posture) since those carry consequences no amount of frontend-code-reading can resolve. Added a "How to use this document" preamble up front: work in §9's dependency order not document order, apply recommended defaults and keep moving, only stop for the explicitly-flagged business/security items, don't invent beyond what's written or directly implied. Frontend side: confirmed nothing else is left. docs/TODO.md already has zero blockers; docs/KNOWN-ISSUES.md's one open item (unreachable Ed25519 error-code UI) is correctly left open and documented rather than faked closed - fixing it needs a real backend emitting real distinguishable error codes, which doesn't exist yet and can't be fabricated without inventing an API contract. No frontend code touched. No architecture changed. No APIs invented.
2026-07-26 16:10:33 +04:00
**Recommended default:** include `scope` in the 503 body (§7 shape);
serve branding for a disabled-but-known tenant so the takeover page can
still be branded (only truly unknown tenants get the unbranded default).
docs: final documentation consolidation - one canonical doc set Audited every *.md in docs/ and root. Merged five overlapping backend docs (BACKEND_INTEGRATION.md + AUTHENTICATION.md + ERROR_CONTRACT.md + MAINTENANCE_MODE.md + the already-archived BACKEND_API.md/ BACKEND_API_REMAINING_WORK.md) into one canonical docs/BACKEND.md (4775 lines, 10 numbered sections) - deleted the four standalone files outright now that their content is fully inlined. Archived (not deleted - real historical value): ADMIN.md (Sprint 19-28 build log, sprint-report-shaped, not a living reference) and FRONTEND-ROADMAP.md (despite its name, a shipped-history changelog with detail no other doc has - not a forward roadmap, so keeping it in root alongside NEXT_PHASE.md was exactly the "10 roadmaps" confusion being cleaned up). Deleted outright (zero value): SPRINTS.md - a leftover copy-pasted sprint-kickoff prompt saved as a file, not documentation. Rewrote docs/PROJECT_STATUS.md with completion-percentage estimates per area (frontend/backend/UI/admin/storefront) and an explicit first-customer-readiness call. Rewrote docs/NEXT_PHASE.md to the strict 5-phase structure (backend integration -> production testing -> performance -> monitoring -> v2 ideas), pointing to PRODUCT_BACKLOG .md/FUTURE_FEATURES.md for phase 5 detail instead of duplicating it. Rewrote root README.md - was stale (referenced deleted pages/info, pages/legal folders from a prior RC pass), now covers architecture, frontend/backend status, how to run, mock<->API switch mechanism (useMockData in environment.ts), current folder structure, and a documentation map. Updated docs/PROJECT_INDEX.md (the stated entry point) to link only the surviving doc set - every remaining document is reachable from it. Fixed every broken/stale cross-reference to the deleted/renamed backend docs across ARCHITECTURE.md, EDITOR.md, FRONTEND.md, PROJECT-STRUCTURE.md, StaticPages.md, KNOWN-ISSUES.md (10 individual link fixes, verified by repo-wide grep before and after). Left CHANGELOG.md's two historical entries untouched - changelogs are append-only history, not live navigation, editing past entries would misrepresent what was true at the time. Not touched (explicitly out of scope): docs/architecture/foundation/** (enforced ADRs/governance, permanent not sprint-shaped), docs/context/** (Barry Cache infrastructure, "do not edit by hand" per CLAUDE.md), .claude/worktrees/** (separate git worktrees containing an unrelated project's docs, not this repo's documentation). docs/ root: 22 files -> 16. Plus 5 in docs/archive/ (was 3).
2026-07-26 14:56:25 +04:00
- §3: none beyond adopting the §7 response shape per-endpoint — this one is mostly
frontend-gap, not backend-undecided.
- §4: which status code is authoritative for read-only (`503` vs `403`) — to be
docs: finalize BACKEND.md as a self-contained implementation prompt Goal: this document alone, pasted into a fresh Claude session, should be enough for a backend dev to implement against without getting stuck or inventing conventions the frontend doesn't actually need. Fixed a real bug: ~65 cross-references throughout the document pointed to docs/AUTHENTICATION.md, docs/ERROR_CONTRACT.md, docs/MAINTENANCE_MODE.md - three sibling docs that were deleted and fully merged into this document's own §4/§6/§10 during the earlier doc-consolidation pass, but the in-text references were never updated. A fresh agent following those links would hit dead ends repeatedly. Bulk-replaced with in- document section references; hand-fixed ~4 sentences that framed §6/ §10 as "sibling task, in progress" (stale - both are complete, this document's §6/§10 already are the settled contract, nothing to wait on). Added "Recommended default" to every item across the document's 5 consolidated "Requires backend decision" registers (§2.12 framework, §3.21 CRUD cross-cutting - 18 items, §4 §12 auth open items - 9 items, §6 error-model summary - 9 items, §10 maintenance-mode list - 5 items). Each default is derived from what the frontend already implies or standard REST/security convention - no invented APIs or business rules. ~6 items are explicitly flagged as real business/security decisions instead (order state-machine rules, Ed25519 cutover strategy, refresh-token reuse-detection posture) since those carry consequences no amount of frontend-code-reading can resolve. Added a "How to use this document" preamble up front: work in §9's dependency order not document order, apply recommended defaults and keep moving, only stop for the explicitly-flagged business/security items, don't invent beyond what's written or directly implied. Frontend side: confirmed nothing else is left. docs/TODO.md already has zero blockers; docs/KNOWN-ISSUES.md's one open item (unreachable Ed25519 error-code UI) is correctly left open and documented rather than faked closed - fixing it needs a real backend emitting real distinguishable error codes, which doesn't exist yet and can't be fabricated without inventing an API contract. No frontend code touched. No architecture changed. No APIs invented.
2026-07-26 16:10:33 +04:00
pinned down jointly with §6.
**Recommended default:** `503` with `code: "READ_ONLY"` — it's a temporary
service-state condition, not a permissions failure, so it belongs in the
5xx family alongside the rest of maintenance mode, not 4xx.
docs: final documentation consolidation - one canonical doc set Audited every *.md in docs/ and root. Merged five overlapping backend docs (BACKEND_INTEGRATION.md + AUTHENTICATION.md + ERROR_CONTRACT.md + MAINTENANCE_MODE.md + the already-archived BACKEND_API.md/ BACKEND_API_REMAINING_WORK.md) into one canonical docs/BACKEND.md (4775 lines, 10 numbered sections) - deleted the four standalone files outright now that their content is fully inlined. Archived (not deleted - real historical value): ADMIN.md (Sprint 19-28 build log, sprint-report-shaped, not a living reference) and FRONTEND-ROADMAP.md (despite its name, a shipped-history changelog with detail no other doc has - not a forward roadmap, so keeping it in root alongside NEXT_PHASE.md was exactly the "10 roadmaps" confusion being cleaned up). Deleted outright (zero value): SPRINTS.md - a leftover copy-pasted sprint-kickoff prompt saved as a file, not documentation. Rewrote docs/PROJECT_STATUS.md with completion-percentage estimates per area (frontend/backend/UI/admin/storefront) and an explicit first-customer-readiness call. Rewrote docs/NEXT_PHASE.md to the strict 5-phase structure (backend integration -> production testing -> performance -> monitoring -> v2 ideas), pointing to PRODUCT_BACKLOG .md/FUTURE_FEATURES.md for phase 5 detail instead of duplicating it. Rewrote root README.md - was stale (referenced deleted pages/info, pages/legal folders from a prior RC pass), now covers architecture, frontend/backend status, how to run, mock<->API switch mechanism (useMockData in environment.ts), current folder structure, and a documentation map. Updated docs/PROJECT_INDEX.md (the stated entry point) to link only the surviving doc set - every remaining document is reachable from it. Fixed every broken/stale cross-reference to the deleted/renamed backend docs across ARCHITECTURE.md, EDITOR.md, FRONTEND.md, PROJECT-STRUCTURE.md, StaticPages.md, KNOWN-ISSUES.md (10 individual link fixes, verified by repo-wide grep before and after). Left CHANGELOG.md's two historical entries untouched - changelogs are append-only history, not live navigation, editing past entries would misrepresent what was true at the time. Not touched (explicitly out of scope): docs/architecture/foundation/** (enforced ADRs/governance, permanent not sprint-shaped), docs/context/** (Barry Cache infrastructure, "do not edit by hand" per CLAUDE.md), .claude/worktrees/** (separate git worktrees containing an unrelated project's docs, not this repo's documentation). docs/ root: 22 files -> 16. Plus 5 in docs/archive/ (was 3).
2026-07-26 14:56:25 +04:00
- §5: whether scheduled-maintenance notice ships via a `bootstrap.maintenanceNotice`
field (proposed) or a separate polling endpoint.
docs: finalize BACKEND.md as a self-contained implementation prompt Goal: this document alone, pasted into a fresh Claude session, should be enough for a backend dev to implement against without getting stuck or inventing conventions the frontend doesn't actually need. Fixed a real bug: ~65 cross-references throughout the document pointed to docs/AUTHENTICATION.md, docs/ERROR_CONTRACT.md, docs/MAINTENANCE_MODE.md - three sibling docs that were deleted and fully merged into this document's own §4/§6/§10 during the earlier doc-consolidation pass, but the in-text references were never updated. A fresh agent following those links would hit dead ends repeatedly. Bulk-replaced with in- document section references; hand-fixed ~4 sentences that framed §6/ §10 as "sibling task, in progress" (stale - both are complete, this document's §6/§10 already are the settled contract, nothing to wait on). Added "Recommended default" to every item across the document's 5 consolidated "Requires backend decision" registers (§2.12 framework, §3.21 CRUD cross-cutting - 18 items, §4 §12 auth open items - 9 items, §6 error-model summary - 9 items, §10 maintenance-mode list - 5 items). Each default is derived from what the frontend already implies or standard REST/security convention - no invented APIs or business rules. ~6 items are explicitly flagged as real business/security decisions instead (order state-machine rules, Ed25519 cutover strategy, refresh-token reuse-detection posture) since those carry consequences no amount of frontend-code-reading can resolve. Added a "How to use this document" preamble up front: work in §9's dependency order not document order, apply recommended defaults and keep moving, only stop for the explicitly-flagged business/security items, don't invent beyond what's written or directly implied. Frontend side: confirmed nothing else is left. docs/TODO.md already has zero blockers; docs/KNOWN-ISSUES.md's one open item (unreachable Ed25519 error-code UI) is correctly left open and documented rather than faked closed - fixing it needs a real backend emitting real distinguishable error codes, which doesn't exist yet and can't be fabricated without inventing an API contract. No frontend code touched. No architecture changed. No APIs invented.
2026-07-26 16:10:33 +04:00
**Recommended default:** the `bootstrap.maintenanceNotice` field — it
rides the existing bootstrap fetch/cache lifecycle for free; a separate
polling endpoint adds a new recurring request for no added value.
docs: final documentation consolidation - one canonical doc set Audited every *.md in docs/ and root. Merged five overlapping backend docs (BACKEND_INTEGRATION.md + AUTHENTICATION.md + ERROR_CONTRACT.md + MAINTENANCE_MODE.md + the already-archived BACKEND_API.md/ BACKEND_API_REMAINING_WORK.md) into one canonical docs/BACKEND.md (4775 lines, 10 numbered sections) - deleted the four standalone files outright now that their content is fully inlined. Archived (not deleted - real historical value): ADMIN.md (Sprint 19-28 build log, sprint-report-shaped, not a living reference) and FRONTEND-ROADMAP.md (despite its name, a shipped-history changelog with detail no other doc has - not a forward roadmap, so keeping it in root alongside NEXT_PHASE.md was exactly the "10 roadmaps" confusion being cleaned up). Deleted outright (zero value): SPRINTS.md - a leftover copy-pasted sprint-kickoff prompt saved as a file, not documentation. Rewrote docs/PROJECT_STATUS.md with completion-percentage estimates per area (frontend/backend/UI/admin/storefront) and an explicit first-customer-readiness call. Rewrote docs/NEXT_PHASE.md to the strict 5-phase structure (backend integration -> production testing -> performance -> monitoring -> v2 ideas), pointing to PRODUCT_BACKLOG .md/FUTURE_FEATURES.md for phase 5 detail instead of duplicating it. Rewrote root README.md - was stale (referenced deleted pages/info, pages/legal folders from a prior RC pass), now covers architecture, frontend/backend status, how to run, mock<->API switch mechanism (useMockData in environment.ts), current folder structure, and a documentation map. Updated docs/PROJECT_INDEX.md (the stated entry point) to link only the surviving doc set - every remaining document is reachable from it. Fixed every broken/stale cross-reference to the deleted/renamed backend docs across ARCHITECTURE.md, EDITOR.md, FRONTEND.md, PROJECT-STRUCTURE.md, StaticPages.md, KNOWN-ISSUES.md (10 individual link fixes, verified by repo-wide grep before and after). Left CHANGELOG.md's two historical entries untouched - changelogs are append-only history, not live navigation, editing past entries would misrepresent what was true at the time. Not touched (explicitly out of scope): docs/architecture/foundation/** (enforced ADRs/governance, permanent not sprint-shaped), docs/context/** (Barry Cache infrastructure, "do not edit by hand" per CLAUDE.md), .claude/worktrees/** (separate git worktrees containing an unrelated project's docs, not this repo's documentation). docs/ root: 22 files -> 16. Plus 5 in docs/archive/ (was 3).
2026-07-26 14:56:25 +04:00
- §7: how this document's 503 body nests inside whatever outer envelope
docs: finalize BACKEND.md as a self-contained implementation prompt Goal: this document alone, pasted into a fresh Claude session, should be enough for a backend dev to implement against without getting stuck or inventing conventions the frontend doesn't actually need. Fixed a real bug: ~65 cross-references throughout the document pointed to docs/AUTHENTICATION.md, docs/ERROR_CONTRACT.md, docs/MAINTENANCE_MODE.md - three sibling docs that were deleted and fully merged into this document's own §4/§6/§10 during the earlier doc-consolidation pass, but the in-text references were never updated. A fresh agent following those links would hit dead ends repeatedly. Bulk-replaced with in- document section references; hand-fixed ~4 sentences that framed §6/ §10 as "sibling task, in progress" (stale - both are complete, this document's §6/§10 already are the settled contract, nothing to wait on). Added "Recommended default" to every item across the document's 5 consolidated "Requires backend decision" registers (§2.12 framework, §3.21 CRUD cross-cutting - 18 items, §4 §12 auth open items - 9 items, §6 error-model summary - 9 items, §10 maintenance-mode list - 5 items). Each default is derived from what the frontend already implies or standard REST/security convention - no invented APIs or business rules. ~6 items are explicitly flagged as real business/security decisions instead (order state-machine rules, Ed25519 cutover strategy, refresh-token reuse-detection posture) since those carry consequences no amount of frontend-code-reading can resolve. Added a "How to use this document" preamble up front: work in §9's dependency order not document order, apply recommended defaults and keep moving, only stop for the explicitly-flagged business/security items, don't invent beyond what's written or directly implied. Frontend side: confirmed nothing else is left. docs/TODO.md already has zero blockers; docs/KNOWN-ISSUES.md's one open item (unreachable Ed25519 error-code UI) is correctly left open and documented rather than faked closed - fixing it needs a real backend emitting real distinguishable error codes, which doesn't exist yet and can't be fabricated without inventing an API contract. No frontend code touched. No architecture changed. No APIs invented.
2026-07-26 16:10:33 +04:00
§6 defines.
**Recommended default:** already resolved — nest under the §6 envelope
(`{ error: { code, message, status, requestId, ... } }`) as shown in this
section's JSON examples; no separate top-level shape.
docs: final documentation consolidation - one canonical doc set Audited every *.md in docs/ and root. Merged five overlapping backend docs (BACKEND_INTEGRATION.md + AUTHENTICATION.md + ERROR_CONTRACT.md + MAINTENANCE_MODE.md + the already-archived BACKEND_API.md/ BACKEND_API_REMAINING_WORK.md) into one canonical docs/BACKEND.md (4775 lines, 10 numbered sections) - deleted the four standalone files outright now that their content is fully inlined. Archived (not deleted - real historical value): ADMIN.md (Sprint 19-28 build log, sprint-report-shaped, not a living reference) and FRONTEND-ROADMAP.md (despite its name, a shipped-history changelog with detail no other doc has - not a forward roadmap, so keeping it in root alongside NEXT_PHASE.md was exactly the "10 roadmaps" confusion being cleaned up). Deleted outright (zero value): SPRINTS.md - a leftover copy-pasted sprint-kickoff prompt saved as a file, not documentation. Rewrote docs/PROJECT_STATUS.md with completion-percentage estimates per area (frontend/backend/UI/admin/storefront) and an explicit first-customer-readiness call. Rewrote docs/NEXT_PHASE.md to the strict 5-phase structure (backend integration -> production testing -> performance -> monitoring -> v2 ideas), pointing to PRODUCT_BACKLOG .md/FUTURE_FEATURES.md for phase 5 detail instead of duplicating it. Rewrote root README.md - was stale (referenced deleted pages/info, pages/legal folders from a prior RC pass), now covers architecture, frontend/backend status, how to run, mock<->API switch mechanism (useMockData in environment.ts), current folder structure, and a documentation map. Updated docs/PROJECT_INDEX.md (the stated entry point) to link only the surviving doc set - every remaining document is reachable from it. Fixed every broken/stale cross-reference to the deleted/renamed backend docs across ARCHITECTURE.md, EDITOR.md, FRONTEND.md, PROJECT-STRUCTURE.md, StaticPages.md, KNOWN-ISSUES.md (10 individual link fixes, verified by repo-wide grep before and after). Left CHANGELOG.md's two historical entries untouched - changelogs are append-only history, not live navigation, editing past entries would misrepresent what was true at the time. Not touched (explicitly out of scope): docs/architecture/foundation/** (enforced ADRs/governance, permanent not sprint-shaped), docs/context/** (Barry Cache infrastructure, "do not edit by hand" per CLAUDE.md), .claude/worktrees/** (separate git worktrees containing an unrelated project's docs, not this repo's documentation). docs/ root: 22 files -> 16. Plus 5 in docs/archive/ (was 3).
2026-07-26 14:56:25 +04:00
### No frontend UI currently exists for this — requires a future frontend task (full list)
- Global maintenance full-page takeover component.
- Per-tenant maintenance takeover (branded or not, pending §2's backend decision).
- Per-module inline maintenance banner/empty-state wiring on checkout/payment flows.
- Proactive read-only disabling of write controls (Add to cart / Submit review /
Submit question / Checkout) ahead of a failed request.
- Scheduled-maintenance banner + countdown component at the layout/header level.
- Richer "temporarily unavailable, back at X" messaging for `featureFlags`-gated
features (today they just silently don't render — no explanatory copy).
---
docs(backend): add Seller Management section to BACKEND.md docs/BACKEND_API.md no longer exists as a live file (merged into BACKEND.md in an earlier consolidation pass, per that doc's own intro - only docs/archive/BACKEND_API.md remains, historical only). This mission's "update BACKEND_API.md" instruction is fulfilled by extending the doc that actually supersedes it: new §11 "Seller Management (Optional Capability)", added to the top-of-file table of contents, no existing section renumbered or altered. Every subsection explicitly tagged Implemented / Planned / Future, matching the same legend used in docs/architecture/foundation/ Seller-Management.md (the frontend-side capability doc this section is the backend counterpart to): - 11.2 Future entities: Marketplace (Implemented, existing TenantConfig unchanged), Seller/SellerBranding (Planned - frontend types exist, no backend schema), SellerUser/SellerSettings/ SellerInvitation (Future - no type, no concept, named for roadmap completeness only). - 11.3 Future endpoints: Seller CRUD/Activation/Invitations/Branding/ Analytics/Dashboard - all Future, none designed, each noted as following the existing mock-to-API-gateway pattern (SS8) once built. - 11.4 Authentication: SellerPermissionRole (4 roles) explicitly flagged as a separate vocabulary from the existing, live AdminRole - not merged, no guard wired, zero auth change. - 11.5 Domain resolution: market.com -> Marketplace is Implemented today (ADR-001, backend-Host-resolved); nike.market.com -> Marketplace -> Seller is Future, no backend resolves it - and per the storefront audit, needs no frontend routing change once it does. - 11.6 Bootstrap additions: modules/modules.sellerManagement documented as Implemented-as-contract (typed, always false/absent today); sellerScope/sellerBranding as Planned with an explicitly flagged open question (SellerConfig vs SellerBranding nesting not reconciled); permissions noted as existing/unrelated today. - 11.7 Checkout modes: Unified Order vs Split Orders - Future, not designed, flagged as the single most consequential undecided item for backend design given payments/refunds/reporting all depend on it. - 11.8 Product ownership: sellerId? on Item/AdminProduct/AdminOrder - Implemented as schema only (optional, absent = marketplace-owned, verified backward-compatible via tsc staying clean). Existing products remain valid with no migration required - NULL/absent ownership documented as a permanent state, not transitional. No backend implemented. No frontend code touched. Documentation only.
2026-07-26 22:41:57 +04:00
## 11. Seller Management (Optional Capability)
Status legend for this section only (matches
`docs/architecture/foundation/Seller-Management.md`, the frontend-side
capability doc this section is the backend counterpart to):
- **Implemented** — exists in frontend source today, verified.
- **Planned** — a typed contract or explicit ADR decision exists; no backend
or runtime behavior exists yet.
- **Future** — named for roadmap completeness only. No shape, contract, or
decision exists. Do not build against a Future item without a new ADR.
**Nothing in this section is implemented on the backend, because no backend
exists yet for anything in this document.** Frontend-side preparation
(typed models, a disabled-by-default feature flag, one Backoffice
placeholder page) is real and is marked Implemented per item below; backend
work is entirely Planned or Future. This section documents the frontend's
existing preparation and the shape a backend should target — it does not
invent new frontend behavior, and per this section's mission, it must not
be read as backend implementation guidance beyond documentation.
### 11.1 Overview
Seller Management is an optional platform capability: a marketplace
(tenant) may host zero or more independent sellers, each scoped beneath it.
It is not a second tenancy tier — see §11.5 Domain Resolution. Full
frontend-side detail: `docs/architecture/foundation/Seller-Management.md`
and its companion ADR-011.
### 11.2 Future entities
None of these have a backend schema, migration, or storage decision yet.
Frontend typed equivalents (where they exist) are cited per entity.
- **Marketplace** — **Implemented** (existing `TenantConfig`, unchanged by
this work). The plus a new **Planned** frontend-only reference type,
`MarketplaceRef` (`core/sellers/models/marketplace-ref.model.ts`):
`{id, slug, name}`, the minimal view of a marketplace as seen from a
seller record. Not a backend entity of its own — it's a projection of
the existing Marketplace/tenant entity.
- **Seller** — **Planned** (frontend type exists: `Seller`,
`core/sellers/models/seller.model.ts``id`, `marketplace: MarketplaceRef`,
`name`, `slug`, `status: SellerStatus`, optional `branding: SellerBranding`,
`createdAt`/`updatedAt`). No backend table, migration, or endpoint exists.
- **SellerUser** — **Future**. No frontend type exists for a user record
scoped to a specific seller (distinct from the existing marketplace-level
`AdminUser`, `features/admin/users/models/admin-user.model.ts`). Named
here for roadmap completeness only — shape not decided.
- **SellerBranding** — **Planned** (frontend type exists: `SellerBranding`,
`core/sellers/models/seller-branding.model.ts` — logo, banner,
description, contacts, address, theme overrides, every field optional).
No backend storage or rendering/precedence logic exists (frontend side of
precedence — falling back to marketplace branding — is also undecided,
per `Seller-Management.md` §6).
- **SellerSettings** — **Future**. No frontend type, no backend concept.
Distinct from `SellerBranding` (visual) — would cover operational
settings (e.g. notification preferences, payout details) if this
capability is ever built out. Not designed.
- **SellerInvitation** — **Future**. No frontend type, no backend concept.
The onboarding mechanism implied by the existing Phase 1 UI's "Request
Access" form (`features/admin/seller-management/pages/
admin-seller-management-page.component.ts`) is a **mocked, client-side-only
submission today** — no email is sent, no invitation record is created,
nothing persists past the browser session. A real invitation flow
(backend-issued invitation token, acceptance, seller-account creation) is
entirely Future.
### 11.3 Future endpoints
No endpoint below exists. Each would follow this codebase's established
mock↔API-gateway pattern (§8) once built — that pattern is a statement of
intent here, not a designed URL/DTO contract.
- **Seller CRUD** — **Future**. No `SellerRepository`/`SellerGateway`
interface exists yet (unlike e.g. Categories, §3.2, which already has
this seam). Would need `list/get/create/update/delete` following the
conventions in §2 (pagination, filtering, error envelope) once designed.
- **Seller Activation** — **Future**. Ties to `SellerStatus`
(`'pending' | 'active' | 'suspended' | 'disabled'`, frontend type only,
no transition logic anywhere) and to SellerInvitation above. No
state-machine, no endpoint, no decision on who can activate/suspend a
seller.
- **Seller Invitations** — **Future**. See SellerInvitation above — this
would be the endpoint surface for that entity, once it exists.
- **Seller Branding** — **Future** (endpoint). The `SellerBranding` type is
Planned (§11.2) but no read/write endpoint exists to serve or persist it.
- **Seller Analytics** — **Future**. The existing `AdminAnalyticsFacade`
(§3.20) already has no real aggregation endpoint for the marketplace as a
whole — per-seller analytics is a further layer on top of a gap that
isn't solved yet even without sellers. Not designed.
- **Seller Dashboard** — **Future**. Same reasoning as Seller Analytics;
the existing `AdminDashboardMetricsGateway.loadMetrics()` (§3, Dashboard)
is a bare no-arg call with no parameter to scope by seller even
syntactically — a seller dashboard is new surface, not an extension.
### 11.4 Authentication
**No authentication change of any kind exists in this work.** The four
roles below are a **Planned** vocabulary only
(`SellerPermissionRole`, `core/sellers/models/seller-permissions.model.ts`):
`marketplaceOwner`, `seller`, `sellerStaff`, `platformAdmin`. This is
**explicitly a separate vocabulary from the existing, live `AdminRole`**
(Owner/Manager/Support/ReadOnly, `core/auth/models/permission.model.ts`,
§4) — not merged, not wired into any guard, no route currently checks it.
Closest existing hook (per
`Seller-Management-Backoffice-Readiness-Audit.md`): `AdminUser` already
carries an `AdminUserScope` field (`'marketplace' | 'office'` in seed
data) and a permission-array shape on `AdminRole` — the natural future home
for these four roles, but today it's used for display labels only, nothing
gates on it. **Future:** deciding whether `SellerPermissionRole` extends,
replaces, or sits alongside `AdminRole`; wiring any guard/interceptor to
check it; deciding how a Seller/Seller Staff session authenticates at all
(same Telegram/Ed25519 mechanisms as today, §4, or something new — not
decided).
### 11.5 Domain resolution
```
market.com → Marketplace (Implemented — ADR-001, live today)
nike.market.com → Marketplace → Seller (Future — no backend resolves this yet)
```
**Implemented today:** tenant (Marketplace) resolution is entirely
backend-side by request Host (ADR-001, §1.9). The frontend never resolves
tenant identity itself — it calls `GET /bootstrap` and renders whatever
comes back for whatever hostname it's running on.
**Future:** a second-level Host pattern (`{seller}.{marketplace-domain}`)
resolving to a seller scope beneath that marketplace. No backend logic for
this exists. Per the frontend-side storefront audit
(`Seller-Management-Storefront-Audit.md`), the frontend's existing
discipline — nothing hardcodes hostname, canonical URLs already derive
from `location.origin` dynamically — means this pattern needs **no
frontend routing change** once a backend resolves it; the open work is
entirely backend resolution logic plus which bootstrap fields carry the
resolved seller data (see §11.6).
### 11.6 Bootstrap additions
All **Implemented as typed contracts, always absent/false in every real
bootstrap response today** — no backend populates any of these fields
anywhere:
- **`modules`** — `BootstrapConfig.modules?: PlatformModulesConfig`
(`shared/models/config/platform-modules.model.ts`). Optional; absent
means every module is disabled.
- **`modules.sellerManagement`** — `{enabled: boolean}`, defaults to
`false` (`DEFAULT_PLATFORM_MODULES_CONFIG`). Read by the Phase 1 page
today; no backend ever sets it `true`.
- **`sellerScope`** — **Planned**. The frontend has a domain-level
`SellerScope` type (`core/sellers/models/seller-scope.model.ts`:
`{sellerId, marketplaceId}`) and a bootstrap-wire counterpart,
`BootstrapConfig.seller?: SellerConfig`
(`shared/models/config/seller.model.ts`) — present only when the backend
has resolved a seller-level request scope (§11.5). No backend does this
yet; the field is optional and always absent today.
- **`sellerBranding`** — **Planned**. Carried as `SellerConfig`'s branding
shape would need to be, or as `BootstrapConfig.seller?.branding` — the
exact nesting isn't finalized, since `SellerConfig` (bootstrap wire
shape) and `SellerBranding` (domain type) are currently separate types
that haven't been reconciled (see `Seller-Management-Domain-Models.md`).
Flagged as an open question here rather than guessed at.
- **`permissions`** — **Implemented** (existing, unchanged) —
`BootstrapConfig.permissions: PermissionsConfig` already exists and is
unrelated to Seller Management's future permission model; the two are
not connected today. **Future:** whether/how `SellerPermissionRole`
(§11.4) ever surfaces through this existing field or a new one — not
decided.
### 11.7 Checkout modes
**Future — not designed.** Two named concepts, no decision between them:
- **Unified Order** — one customer purchase, regardless of how many
sellers' items are in the cart, produces one order record.
- **Split Orders** — one customer purchase spanning multiple sellers
produces one order record per seller.
**Current, unrelated to sellers:** checkout today is a single inline
popup flow (`pages/cart/cart.component.ts`) producing exactly one order via
one `POST /orders`-equivalent call — there is no multi-seller cart concept
of any kind to split or unify, because every product is marketplace-owned.
This decision has real consequences (payments, refunds, reporting) that
depend on it — explicitly listed as open in
`Seller-Management.md` §6 and repeated here because it's the single most
consequential undecided item for backend design once this capability moves
past preparation.
### 11.8 Product ownership
**Implemented (schema only), Future (logic).** `sellerId?: string` was
added as an **optional** field to three existing frontend types: `Item`
(storefront product, `models/item.model.ts`), `AdminProduct`
(`features/admin/products/models/admin-product.model.ts`), and `AdminOrder`
(`features/admin/orders/models/admin-order.model.ts`). In every case,
absent means marketplace-owned — exactly what every existing product and
order is today. **No frontend code reads or writes this field anywhere.**
No backend column, migration, or filter exists for it either.
**Compatibility guarantee, verified not assumed:** adding this optional
field required zero changes to any consumer of these three types —
confirmed by `tsc --noEmit` staying clean after the change
(`Seller-Management-Domain-Models.md`). The same guarantee applies to any
future backend schema change: a nullable `seller_id` column (or
equivalent) that defaults to `NULL`/absent preserves every existing
product/order exactly as-is. **Existing products remain valid** with no
migration required to backfill a value — `NULL`/absent seller ownership is
a first-class, permanent state, not a transitional one.
---
docs: final project closeout - classify TODO, backend spec, status Classified every TODO.md item into one of DONE/BACKEND/PRODUCT DECISION/FUTURE VERSION/BUG, verified against source, not against prior docs: - BACKEND items (bootstrap content, builder draft/publish, 6 admin CRUD domains, media pipeline) confirmed already covered by BACKEND_INTEGRATION.md; appended a mapping appendix rather than duplicating raw bullets. Fixed 22 stale internal BACKEND_API.md cross-references left over from before that file was archived. - PRODUCT DECISION items (dark mode, brand-color WCAG contrast, stars.component token gap, footer Contacts content, advanced analytics, payment providers) moved to new docs/PRODUCT_BACKLOG.md. - FUTURE VERSION items (Angular 22, bundle splitting, cart-modal composition cleanup, hero-spacing investigation) moved to new docs/FUTURE_FEATURES.md. - BUG: rewrote docs/KNOWN-ISSUES.md down to the one real, verified, currently-reproducible frontend bug (Ed25519 admin-auth error codes session-expired/invalid-signature are unreachable - toAuthErrorShape() never reads a body error code, only maps HTTP status, and no status ever produces those two codes - confirmed by reading auth.service.ts + auth-error.model.ts). Condensed the "Fixed" history instead of carrying full verbose repro text forward. - DONE items removed outright (dead-code deletion, dashboard false positive, RC-02 fixes, stale "dynamic-renderer unwired"/"178 missing keys" claims already disproven by source). docs/TODO.md rewritten to the exact "no blockers" template - nothing left qualifies as a release blocker. New docs/PROJECT_STATUS.md: honest per-area status (frontend/backend/ docs/auth/builder/storefront/admin), known limitations, and explicit production/backend/demo readiness calls - including correcting an initial draft's unpushed-commit count (53, not 10, per git log origin/B2B..HEAD). New docs/NEXT_PHASE.md: work that can only start once a real backend exists (gateway swap-in, mock removal, dormant-auth activation, role enforcement, integration/E2E tests, perf profiling, monitoring, maintenance-mode UI). docs/PROJECT_INDEX.md (the stated entry point) updated to link the new doc set and stop pointing at the now-archived BACKEND_API.md/AUTH.md. docs/FRONTEND-ROADMAP.md's "Known open items" replaced with pointers to the new category-split docs instead of a duplicated mixed list. Not swept: a handful of low-traffic docs (architecture ADRs, FRONTEND.md, EDITOR.md, ARCHITECTURE.md, PROJECT-STRUCTURE.md, StaticPages.md, ADMIN.md) still reference the old BACKEND_API.md/ AUTH.md filenames - noted as a known gap in PROJECT_STATUS.md rather than touched blindly, since they're historical-context docs, not the navigation entry point.
2026-07-26 12:35:26 +04:00
## Appendix: `docs/TODO.md` items merged into this document (2026-07-26)
Final Project Closeout moved every backend-shaped item out of `docs/TODO.md` into this
document. None were duplicated as raw new bullets — each is already covered by an
existing section above:
| TODO item | Covered by |
|---|---|
| `bootstrap.json` real content (branding/theme/nav/seo) | §1 Bootstrap |
| Builder bootstrap draft/publish/validate | §1 (Draft vs Published), §8, §9 Phase 5 |
| Backoffice Products CRUD | §3 Products, §8, §9 Phase 4 |
| Media upload/delete/replace pipeline | §7 Uploads, §9 Phase 5 |
| Backoffice Orders CRUD + status transitions | §3 Orders, §8, §9 Phase 4 |
| Backoffice Transactions | §3 Transactions, §9 Phase 4 |
| Backoffice Users/roles/invitations | §3 Users/Roles, §9 Phase 4 |
| Backoffice Moderation (reviews/reports) | §3 Reviews/Reports, §9 Phase 4 |
| Backend Ready sprint / no real API contract | This entire document |
`docs/TODO.md` is now empty of blockers — see that file.