From f6045a07b2b93568c82199ebb9e9d39ffd7922a6 Mon Sep 17 00:00:00 2001 From: sdarbinyan Date: Tue, 18 Aug 2026 01:46:58 +0400 Subject: [PATCH] docs: backend handoff, package usage guide, finalized CI/CD - docs/backend/BACKEND-HANDOFF.md: single entry point for a backend dev - reading order, verified infrastructure state (nginx running, Postgres inactive, no API on :8080, no TLS, no DNS automation, no CI runner), auth surface, and the day-one setup that is still outstanding - docs/PACKAGES-USAGE.md: install, required DI providers, full exported API for both auth mechanisms, and how to ship a package change - PACKAGE-EXTRACTION.md now covers build/release/infra only and points at the usage guide; CI section reflects the two real workflows Co-Authored-By: Claude Sonnet 5 --- docs/PACKAGE-EXTRACTION.md | 17 +++- docs/PACKAGES-USAGE.md | 136 ++++++++++++++++++++++++++++++++ docs/backend/BACKEND-HANDOFF.md | 69 ++++++++++++++++ docs/backend/README.md | 2 + 4 files changed, 222 insertions(+), 2 deletions(-) create mode 100644 docs/PACKAGES-USAGE.md create mode 100644 docs/backend/BACKEND-HANDOFF.md diff --git a/docs/PACKAGE-EXTRACTION.md b/docs/PACKAGE-EXTRACTION.md index 68cf25d..e08cfab 100644 --- a/docs/PACKAGE-EXTRACTION.md +++ b/docs/PACKAGE-EXTRACTION.md @@ -1,6 +1,6 @@ # @marketplaces/auth & @marketplaces/payment — build, version, publish, consume -See [ADR-0001](context/adrs/ADR-0001-extract-auth-and-payment-into-shared-marketplaces-packages.md) for why. This doc is the how. +See [ADR-0001](context/adrs/ADR-0001-extract-auth-and-payment-into-shared-marketplaces-packages.md) for why. This doc is the how — build, release, and infrastructure. For *consuming* the packages (install, providers, exported API), see [PACKAGES-USAGE.md](PACKAGES-USAGE.md). ## Current state @@ -30,7 +30,20 @@ Follow-up decision needed before CI can publish/consume without a human at the k ## 4. Publishing (CI) -`vitanovaPackages/.github/workflows/release.yml`: on push to `main`, installs, builds, tests, then `changesets/action` — opens/updates a version-bump PR if unreleased changesets exist, publishes once that PR merges. Needs `NPM_TOKEN` (Verdaccio token) and `GITHUB_TOKEN` as repo secrets; also needs CI to reach the registry, which circles back to §2's open follow-up. Until that's resolved, publish manually the same way this session did it: build (`tsc`), `npm publish --registry http://127.0.0.1:4873/` through the tunnel. +Two workflows in the vitanovaPackages repo: + +- `ci.yml` — on PRs and non-main pushes: install, build, test, and reject the PR if it has no changeset. +- `release.yml` — on push to `main`: install, build, test, then `changesets/action` opens/updates a version-bump PR; merging that PR publishes. + +`release.yml` needs repo secrets `NPM_REGISTRY_URL` and `NPM_TOKEN`. **Neither is set, because no CI runner can currently reach the registry** (§2). The workflow fails loudly at the auth step rather than silently skipping the publish — that's deliberate, so a broken release is visible instead of looking green. + +Until it's resolved, publish manually through the tunnel: + +```bash +npm login --registry=http://127.0.0.1:4873/ --scope=@marketplaces +npm run build +cd packages/auth && npm publish --registry http://127.0.0.1:4873/ +``` ## 5. Consuming from `marketplaces` (and other projects) diff --git a/docs/PACKAGES-USAGE.md b/docs/PACKAGES-USAGE.md new file mode 100644 index 0000000..748c7e5 --- /dev/null +++ b/docs/PACKAGES-USAGE.md @@ -0,0 +1,136 @@ +# Using `@marketplaces/auth` and `@marketplaces/payment` + +How to install and consume the shared packages in `marketplaces` or any other project. For *why* they exist see [ADR-0001](context/adrs/ADR-0001-extract-auth-and-payment-into-shared-marketplaces-packages.md); for how they are built and released see [PACKAGE-EXTRACTION.md](PACKAGE-EXTRACTION.md). + +## 1. Install + +Both packages live on a private Verdaccio registry on the dev server, **not npmjs**. Two things are needed: a scope mapping and an auth token. + +Scope mapping goes in the project's `.npmrc` (already committed in `marketplaces`): + +``` +@marketplaces:registry=http://127.0.0.1:4873/ +``` + +The token is per-developer and **never committed**. Open a tunnel to the registry, then log in once: + +```bash +ssh -L 4873:127.0.0.1:4873 seto@213.21.246.138 +``` + +```bash +npm login --registry=http://127.0.0.1:4873/ --scope=@marketplaces +``` + +Then install normally: + +```bash +npm install @marketplaces/auth +``` + +Versions are pinned exactly (`"@marketplaces/auth": "0.1.0"`, no `^`/`~`) — see [ADR-0001](context/adrs/ADR-0001-extract-auth-and-payment-into-shared-marketplaces-packages.md) on registry-outage blast radius. + +## 2. Required providers + +`@marketplaces/auth` has no knowledge of any specific app's environment config. It reads two injection tokens, both provided by the consuming app in `app.config.ts`: + +```ts +import { AUTH_API_URL, TELEGRAM_BOT_USERNAME } from '@marketplaces/auth'; +import { environment } from '../environments/environment'; + +export const appConfig: ApplicationConfig = { + providers: [ + { provide: AUTH_API_URL, useValue: environment.authApiUrl }, + { provide: TELEGRAM_BOT_USERNAME, useValue: environment.telegramBot }, + // ... + ] +}; +``` + +| Token | Required | Meaning | +|---|---|---| +| `AUTH_API_URL` | yes | Base URL of the auth backend, e.g. `https://api.example.com`. Both auth mechanisms build their endpoints from this. | +| `TELEGRAM_BOT_USERNAME` | no | Bot username for QR/deep-link login URLs. Falls back to a default if absent. | + +Missing `AUTH_API_URL` produces `NG0201: No provider found for InjectionToken @marketplaces/auth AUTH_API_URL` at the first injection — including in unit tests, where any `TestBed` that constructs a component touching auth must provide it: + +```ts +TestBed.configureTestingModule({ + providers: [{ provide: AUTH_API_URL, useValue: 'https://test.local' }], +}); +``` + +## 3. What is in the package + +Two independent auth mechanisms. They deliberately share no state — a customer QR scan never authenticates an admin session or vice versa (distinct cookies, signals, guards, interceptors). + +### `telegram/` — live today + +Telegram QR/session auth against `{AUTH_API_URL}/users/sessions`. One backend endpoint set, used by both customer and admin login; only *storage* differs. + +| Export | What it is | +|---|---| +| `AuthService` | Customer session. Signals: `session`, `status`, `isAuthenticated`, `showLoginDialog`, `displayName`. Methods: `checkSession()`, `createWebSession()`, `requestLogin()`, `hideLogin()`, `logout()`, `onTelegramLoginComplete()`, `getTelegramAppLoginUrl()`. Cookie `webSessionID`, `SameSite=Lax`. | +| `AdminAuthService` | Admin session. Same signal/method shape plus `getAdminToken()`/`setAdminTokens()`/`clearAdminTokens()` (reserved for when the backend issues admin JWTs) and `devBypassLogin()` (no-ops outside dev mode). Cookie `adminSessionID`, `SameSite=Strict`. | +| `TelegramSessionApiService` | Thin HTTP client + response normalization. Holds no state, writes no cookies. | +| `adminAuthGuard` | `CanActivateFn` — allows if the admin session is authenticated, otherwise opens the login dialog. | +| `adminAuthHeadersInterceptor` | Attaches `AdminWebSessionID` (and `Authorization: Bearer` when a token exists) to admin-gated paths only (`/admin/`, `/backoffice/`, `/builder/`, `/media/`). Never touches customer requests. | +| `AuthSession`, `WebSessionStart`, `AuthStatus`, `AdminAuthStatus` | Wire/state types. | + +Typical usage: + +```ts +import { AuthService, AdminAuthService, adminAuthGuard, adminAuthHeadersInterceptor } from '@marketplaces/auth'; + +// routes +{ path: 'backoffice', canActivate: [adminAuthGuard], loadComponent: ... } + +// http +provideHttpClient(withInterceptors([adminAuthHeadersInterceptor, ...])) + +// component +private readonly auth = inject(AuthService); +readonly isLoggedIn = this.auth.isAuthenticated; // signal +``` + +**Security note:** the Telegram session API has no concept of "admin." The frontend cannot distinguish an admin Telegram session from a regular one — it only decides *where to store* the result. Real admin authorization must be enforced server-side on every admin request. See [TRACK-S](backend/TRACK-S-SECURITY-RBAC-CONTRACT.md). + +### `ed25519/` — prepared, backend not shipped + +Challenge/response admin auth: `GET /api/admin/auth/challenge` → sign nonce with a device-local non-extractable Ed25519 key → `POST /api/admin/auth/verify` → JWT pair. Calling these today 404s/connection-errors, which surfaces as the `backend-unavailable` error screen. Nothing is mocked. + +| Export | What it is | +|---|---| +| `AuthFacade` | The surface components should use. `isAuthenticated`, `status`, `role`, `loginPhase`, `lastError`; `login(redirectTo?)`, `logout(redirectTo?)`, `restoreSession()`, `can(permission)`. | +| `Ed25519AuthService` | Low-level flow orchestrator (exported under this name so it doesn't collide with the telegram `AuthService`). | +| `SessionService` | JWT/refresh pair + derived claims, auto-refresh before expiry. | +| `Ed25519KeypairService` | WebCrypto Ed25519 keypair in IndexedDB. Private key is non-extractable and never leaves the device. | +| `PermissionService` | Derives permissions from the JWT `role` claim. UI-only gate. | +| `JwtService` | Decode only, never verification — the frontend has no trusted key; signature checking is the backend's job on every request. | +| `Ed25519VerificationService` / `NoopEd25519VerificationService` | Abstract seam + fail-closed default binding. | +| `AdminRole`, `Permission`, `ROLE_PERMISSIONS`, `AuthChallenge`, `AuthTokenPair`, `JwtClaims`, `AuthError`, `AuthErrorCode`, … | Types and wire contracts. | + +Bind the verification seam in `app.config.ts`: + +```ts +{ provide: Ed25519VerificationService, useClass: NoopEd25519VerificationService }, +``` + +## 4. What deliberately stayed in the app + +`AdminPermissionsService` and `requireAdminPermission` live in `marketplaces` (`src/app/core/admin-auth/`), not in the package. They read this app's mock Users domain to derive a permission set — app-specific, not a portable auth concern. If another project needs permission gating it should use the package's `PermissionService` (JWT-claim-driven) instead. + +## 5. `@marketplaces/payment` + +Published at `0.1.0` but **scaffold only** — no implementation yet, nothing exported, and `marketplaces` does not depend on it. `core/finance` and `core/pricing` still live in the app. Payment business logic is server-side by design (see [Phase 1](backend/PHASE-1-MONEY-FX-PAYMENTS-CONTRACT.md) and [Phase 7](backend/PHASE-7-PAYMENTS-RECONCILIATION-CONTRACT.md)); the eventual package is a thin client for FX/pricing/checkout gateways. + +## 6. Making a change to a package + +1. Clone [vitanovaPackages](https://sources.vitanova.network/sdarbinyan/vitanovaPackages.git). +2. Edit under `packages/auth/src` (or `packages/payment/src`), export from `index.ts`. +3. `npx changeset` at the repo root — pick the package and bump type, write one line about the change. +4. Commit, push, open a PR to `main`. +5. On merge, CI opens a version-bump PR; merging *that* publishes the new version. (Currently blocked — see [PACKAGE-EXTRACTION.md](PACKAGE-EXTRACTION.md) §2/§4 for the registry-reachability follow-up. Until then, publish manually through the tunnel.) +6. In `marketplaces`, bump the pinned version and run the build + test suite before merging. + +Do not edit `node_modules/@marketplaces/*` directly — it is overwritten on every install. diff --git a/docs/backend/BACKEND-HANDOFF.md b/docs/backend/BACKEND-HANDOFF.md new file mode 100644 index 0000000..eccbb4b --- /dev/null +++ b/docs/backend/BACKEND-HANDOFF.md @@ -0,0 +1,69 @@ +# Backend handoff — start here + +Single entry point for a backend developer picking this up cold. Written 2026-08-18. + +## 1. What this is + +`marketplaces` is a multi-tenant marketplace platform frontend (Angular 22). The frontend is built and waiting; **there is no backend yet**. Every wire contract the backend needs to implement is already written and sitting in this directory — see [README.md](README.md) for the full index and build order. + +## 2. Read in this order + +1. [README.md](README.md) — index of all contracts, build order, and what's deliberately excluded. +2. [PHASE-1-MONEY-FX-PAYMENTS-CONTRACT.md](PHASE-1-MONEY-FX-PAYMENTS-CONTRACT.md) — start here. Everything after depends on the money model. +3. Phases 2→4 — the rest of the launch gate (orders, catalog, connectors). +4. [TRACK-S-SECURITY-RBAC-CONTRACT.md](TRACK-S-SECURITY-RBAC-CONTRACT.md) — **gates the launch.** Today the admin role model is decorative: nothing server-side enforces any permission. §8 covers per-marketplace bootstrap admin accounts and self-service sub-admin management. +5. [TRACK-A-ANALYTICS-CONTRACT.md](TRACK-A-ANALYTICS-CONTRACT.md) — longest lead time, start it in parallel with Phase 1. +6. Phases 5→10 — post-launch-gate. + +[../../BACKEND-API-REFERENCE.md](../../BACKEND-API-REFERENCE.md) documents the *current* live API surface (legacy endpoints, error envelope, mock-only areas). New endpoints use `/api/v2/...` namespaces; legacy endpoints are not being migrated. + +## 3. Auth — read before writing any endpoint + +Auth is no longer part of this repo. It lives in `@marketplaces/auth`, published from [vitanovaPackages](https://sources.vitanova.network/sdarbinyan/vitanovaPackages.git). See [../PACKAGES-USAGE.md](../PACKAGES-USAGE.md) for the full client surface. What matters on the backend side: + +**Two mechanisms exist client-side.** + +- **Telegram QR/session (live).** Endpoints under `{authApiUrl}/users/sessions` — `POST` to create, `GET /{id}` to poll, `DELETE /{id}` to log out. Both customer and admin login call the *same* endpoints; only client-side storage differs. The response shape is normalized permissively client-side (many key spellings accepted), but a clean implementation should return `{ webSessionID, user: { userId, username, firstName, lastName }, status, expiresAt }`. +- **Ed25519 challenge/response (not built).** `GET /api/admin/auth/challenge`, `POST /api/admin/auth/verify`, `POST /api/admin/auth/refresh`, `POST /api/admin/auth/logout`. Contracts in [TRACK-S](TRACK-S-SECURITY-RBAC-CONTRACT.md) and the package's `ed25519/models/auth-api.model.ts`. Until these ship, the client shows a `backend-unavailable` screen — nothing is mocked. + +**The critical gap:** the session API has no concept of "admin." The frontend cannot distinguish an admin session from a customer one — it only chooses where to *store* the result. **Every admin endpoint must independently verify authorization server-side.** Client-side guards are UI convenience, never security. This is the single most serious open issue in the system. + +Admin requests carry `AdminWebSessionID: ` (and `Authorization: Bearer ` once admin JWTs exist) on paths containing `/admin/`, `/backoffice/`, `/builder/`, `/media/`. + +## 4. Environment / infrastructure state + +Dev server `213.21.246.138` (user `seto`, sudo, SSH key provided separately). + +| Thing | State | +|---|---| +| nginx 1.24 | **Installed, running.** Config at `/etc/nginx/sites-enabled/marketplaces-dev.conf`. Serves frontend from `/srv/marketplaces/current/frontend`, backoffice from `/srv/marketplaces/current/backoffice`, proxies `/api/` → `127.0.0.1:8080`. `/health` returns `ok`. | +| Go toolchain | Installed (`/usr/local/bin/go`). | +| Backend service on :8080 | **Not running.** Nothing is listening. `/srv/marketplaces/current/api` is an empty shell. nginx's `/api/` proxy currently 502s. | +| PostgreSQL | **Installed but inactive.** Needs starting, a database, a user, and schema before anything works. | +| Verdaccio (npm registry) | Running in Docker, port 4873, storage `/srv/marketplaces/verdaccio/`. Hosts `@marketplaces/auth@0.1.0` and `@marketplaces/payment@0.1.0`. **Only reachable from the server itself or via SSH tunnel** — the firewall allows 80/443/SSH only. | +| Firewall (ufw) | Active. 80/tcp, 443/tcp, OpenSSH. | +| TLS / certbot | **Not installed.** No certificates. Everything is plain HTTP today. | +| DNS / dynamic subdomains | **Not set up.** The server has no domain pointed at it (reverse DNS is the provider default `silky-bronze.ptr.network`). There is no wildcard record, no per-tenant subdomain automation, and no Hostinger DNS integration. [PHASE-9](PHASE-9-TENANT-REGISTRY-DOMAINS-CONTRACT.md) specifies what this should become — none of it exists yet. | +| CI runner | None on this server. `sources.vitanova.network` CI runs elsewhere and currently cannot reach the Verdaccio registry. | + +## 5. To get a working dev environment + +Nothing here is done yet — this is the setup a backend dev does on day one. + +1. Start and configure PostgreSQL; create the database and application user. +2. Design the schema from the Phase 1–4 contracts (schema design is explicitly the backend's own call — the contracts specify entities, endpoints, and invariants, never tables). +3. Build the API service, listen on `127.0.0.1:8080`. nginx already proxies `/api/` to it. +4. Implement the Telegram session endpoints first — the frontend's login flow is fully built and blocked only on these. +5. Implement `GET /api/identity/v1/session/permissions` ([TRACK-S §2](TRACK-S-SECURITY-RBAC-CONTRACT.md)) — frontend route guards derive from it. +6. Seed per-marketplace bootstrap admins ([TRACK-S §8](TRACK-S-SECURITY-RBAC-CONTRACT.md)): login = marketplace slug, password = `{slug}2026$`, `mustChangePassword: true`. + +## 6. Frontend deploy + +The frontend builds with `npm run build` (Angular 22, Node 20+). Output goes to `dist/dexarmarket`, which is what nginx serves from `/srv/marketplaces/current/frontend`. Building it requires registry access for `@marketplaces/auth` — see [../PACKAGES-USAGE.md](../PACKAGES-USAGE.md) §1. **A fresh `npm install` on a machine without a registry token will fail.** That is the first thing to fix for anyone new joining. + +## 7. Known open decisions + +- Registry reachability for CI (reverse proxy + TLS, or a different registry entirely). +- Backend ownership was still unnamed as of Sprint 0.1. +- Additional payment providers (wallets, BNPL) — [Phase 7 §4](PHASE-7-PAYMENTS-RECONCILIATION-CONTRACT.md). +- Per-connector marketplace adapters — written per partner at onboarding, [Phase 4 §8](PHASE-4-CONNECTOR-FRAMEWORK-CONTRACT.md). diff --git a/docs/backend/README.md b/docs/backend/README.md index 1c61aa0..6501090 100644 --- a/docs/backend/README.md +++ b/docs/backend/README.md @@ -1,5 +1,7 @@ # Backend Contracts Index — Product Plan v3.1 +> **New here? Start with [BACKEND-HANDOFF.md](BACKEND-HANDOFF.md)** — reading order, current infrastructure state, auth surface, and what a working dev environment still needs. + This directory is the complete set of wire contracts for building the backend behind [Product Plan v3.1](../PRODUCT-PLAN-v3.1-GAP-ANALYSIS.md). Each doc specifies entities, endpoints, and invariants only — never DB schema or service boundaries, which stay backend's own call. **Read order matches build order.** Every doc after Phase 1 depends on the ones before it (noted at the top of each). All Sprint 0.1 decisions referenced throughout were answered 2026-08-17 — see [PRODUCT-PLAN-v3.1-DELIVERY-PLAN.md](../PRODUCT-PLAN-v3.1-DELIVERY-PLAN.md) Sprint 0.1 for the full record.