From 14c72d1a6a8021eb3a1870b9032092bb2a9e152b Mon Sep 17 00:00:00 2001 From: sdarbinyan Date: Tue, 18 Aug 2026 01:05:16 +0400 Subject: [PATCH] feat: extract auth into @marketplaces/auth package, add backoffice admin provisioning spec MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - ADR-0001: decision to extract auth/payment into shared @marketplaces/* packages - Scaffold packages/auth, packages/payment; @marketplaces/auth now holds the real telegram (customer+admin QR/session) and ed25519 (future admin challenge/response) auth implementation, pushed to sources.vitanova.network/sdarbinyan/vitanovaPackages - Rewire ~30 call sites to import from @marketplaces/auth; delete migrated originals from core/auth, core/admin-auth, services/, models/ - Replace environment coupling with AUTH_API_URL/TELEGRAM_BOT_USERNAME injection tokens and isDevMode(); wired as file:packages/auth pending registry publish - Add TRACK-S §8: bootstrap per-marketplace admin login + marketplace-scoped sub-admin invite/role endpoints - Build, arch:check:boundaries, and full test suite (103/103) all green Co-Authored-By: Claude Sonnet 5 --- .gitignore | 1 + docs/PACKAGE-EXTRACTION.md | 50 +++++++++++++++++++ .../backend/TRACK-S-SECURITY-RBAC-CONTRACT.md | 30 ++++++++++- ...yment-into-shared-marketplaces-packages.md | 38 ++++++++++++++ .../features/platform-vision/FACTS.jsonl | 1 + package-lock.json | 15 ++++++ package.json | 1 + packages/README.md | 11 ++++ packages/auth/package.json | 21 ++++++++ packages/auth/src/config.ts | 7 +++ .../auth/src/ed25519}/auth-api.service.ts | 16 +++--- .../auth/src/ed25519}/auth-facade.service.ts | 2 +- .../auth/src/ed25519}/auth.service.ts | 8 +-- .../src/ed25519}/ed25519-keypair.service.ts | 0 .../ed25519}/ed25519-verification.model.ts | 0 .../auth/src/ed25519}/jwt.service.ts | 2 +- .../src/ed25519}/models/auth-api.model.ts | 9 +--- .../src/ed25519}/models/auth-error.model.ts | 11 ++-- .../src/ed25519}/models/permission.model.ts | 16 ++---- .../noop-ed25519-verification.service.ts | 0 .../auth/src/ed25519}/permission.service.ts | 4 +- .../auth/src/ed25519}/session.service.ts | 8 +-- packages/auth/src/index.ts | 43 ++++++++++++++++ .../admin-auth-headers.interceptor.ts | 9 ++-- .../auth/src/telegram/admin-auth.guard.ts | 15 ++++++ .../auth/src/telegram}/admin-auth.service.ts | 36 +++++++------ .../auth/src/telegram}/auth.service.ts | 3 +- .../auth/src/telegram/models/session.model.ts | 1 + .../telegram}/telegram-session-api.service.ts | 17 ++++--- packages/auth/src/util/guid.util.ts | 21 ++++++++ packages/auth/tsconfig.json | 15 ++++++ packages/payment/package.json | 21 ++++++++ packages/payment/src/index.ts | 5 ++ packages/payment/tsconfig.json | 15 ++++++ renovate.json | 12 +++++ src/app/app.config.ts | 7 +-- src/app/app.routes.ts | 3 +- src/app/app.ts | 3 +- .../header/header.component.spec.ts | 3 +- src/app/components/header/header.component.ts | 2 +- .../telegram-login.component.ts | 4 +- src/app/core/admin-auth/admin-auth.guard.ts | 22 +++----- .../admin-auth/admin-permissions.service.ts | 2 +- .../auth/pages/admin-login-page.component.ts | 3 +- .../auth/pages/auth-error-page.component.ts | 2 +- .../providers/api-product-data.provider.ts | 2 +- .../facade/admin-dashboard.facade.ts | 2 +- .../services/admin-orders-local.gateway.ts | 2 +- .../shell/admin-layout.component.spec.ts | 2 + .../admin/shell/admin-layout.component.ts | 2 +- .../admin-order-watcher.service.spec.ts | 2 +- .../services/admin-order-watcher.service.ts | 2 +- .../admin-transactions-local.gateway.ts | 2 +- .../services/admin-users-local.gateway.ts | 2 +- .../search/services/search-history.service.ts | 2 +- .../product-details-container.component.ts | 2 +- .../interceptors/api-headers.interceptor.ts | 2 +- .../dynamic-page-layout.component.ts | 2 +- src/app/models/admin-auth.model.ts | 1 - src/app/models/index.ts | 1 - src/app/pages/cart/cart.component.ts | 3 +- src/app/services/index.ts | 1 - 62 files changed, 420 insertions(+), 127 deletions(-) create mode 100644 docs/PACKAGE-EXTRACTION.md create mode 100644 docs/context/adrs/ADR-0001-extract-auth-and-payment-into-shared-marketplaces-packages.md create mode 100644 packages/README.md create mode 100644 packages/auth/package.json create mode 100644 packages/auth/src/config.ts rename {src/app/core/auth/services => packages/auth/src/ed25519}/auth-api.service.ts (67%) rename {src/app/core/auth/services => packages/auth/src/ed25519}/auth-facade.service.ts (96%) rename {src/app/core/auth/services => packages/auth/src/ed25519}/auth.service.ts (94%) rename {src/app/core/auth/services => packages/auth/src/ed25519}/ed25519-keypair.service.ts (100%) rename {src/app/core/admin-auth => packages/auth/src/ed25519}/ed25519-verification.model.ts (100%) rename {src/app/core/auth/services => packages/auth/src/ed25519}/jwt.service.ts (96%) rename {src/app/core/auth => packages/auth/src/ed25519}/models/auth-api.model.ts (74%) rename {src/app/core/auth => packages/auth/src/ed25519}/models/auth-error.model.ts (74%) rename {src/app/core/auth => packages/auth/src/ed25519}/models/permission.model.ts (58%) rename {src/app/core/admin-auth => packages/auth/src/ed25519}/noop-ed25519-verification.service.ts (100%) rename {src/app/core/auth/services => packages/auth/src/ed25519}/permission.service.ts (83%) rename {src/app/core/auth/services => packages/auth/src/ed25519}/session.service.ts (93%) create mode 100644 packages/auth/src/index.ts rename {src/app/core/admin-auth => packages/auth/src/telegram}/admin-auth-headers.interceptor.ts (77%) create mode 100644 packages/auth/src/telegram/admin-auth.guard.ts rename {src/app/core/admin-auth => packages/auth/src/telegram}/admin-auth.service.ts (82%) rename {src/app/services => packages/auth/src/telegram}/auth.service.ts (96%) rename src/app/models/auth.model.ts => packages/auth/src/telegram/models/session.model.ts (76%) rename {src/app/services => packages/auth/src/telegram}/telegram-session-api.service.ts (91%) create mode 100644 packages/auth/src/util/guid.util.ts create mode 100644 packages/auth/tsconfig.json create mode 100644 packages/payment/package.json create mode 100644 packages/payment/src/index.ts create mode 100644 packages/payment/tsconfig.json create mode 100644 renovate.json delete mode 100644 src/app/models/admin-auth.model.ts diff --git a/.gitignore b/.gitignore index 6acf860..4f4559f 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ # Compiled output /dist +packages/*/dist /tmp /out-tsc /bazel-out diff --git a/docs/PACKAGE-EXTRACTION.md b/docs/PACKAGE-EXTRACTION.md new file mode 100644 index 0000000..0b3ade7 --- /dev/null +++ b/docs/PACKAGE-EXTRACTION.md @@ -0,0 +1,50 @@ +# @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. + +## Current state + +[packages/](../packages) here is now just a reference copy (`package.json`/`tsconfig.json`, no CI) — the real source, CI, and versioning live in the pushed [vitanovaPackages](https://sources.vitanova.network/sdarbinyan/vitanovaPackages.git) repo (§1). No app code has moved yet — `src/app/core/auth`, `src/app/core/admin-auth`, `src/app/core/finance`, `src/app/core/pricing` are still the live implementation in `marketplaces`. + +## 1. Target repo + +Pushed: [sources.vitanova.network/sdarbinyan/vitanovaPackages](https://sources.vitanova.network/sdarbinyan/vitanovaPackages.git) — single monorepo (npm workspaces) hosting both `packages/auth` and `packages/payment`, `main` branch, initial scaffold commit `5567154`. The `packages/*` scaffold here in `marketplaces` stays as the pre-push staging copy; the pushed repo is now the source of truth for the package source itself. + +## 2. Versioning + +Both packages live in one monorepo (vitanovaPackages, npm workspaces), so versioning uses [Changesets](https://github.com/changesets/changesets), not per-package semantic-release — Changesets is built for exactly this "many packages, one repo, independent versions" shape. A PR that changes `packages/auth` adds a changeset file (`npx changeset` from repo root, picks package + bump type + writes a short description) alongside the code change. + +## 3. Publishing (CI) + +`vitanovaPackages/.github/workflows/release.yml`: on push to `main`, installs, builds, tests, then `changesets/action`: +- if unreleased changesets exist, opens/updates a "Version Packages" PR that bumps `package.json` versions and writes changelogs, +- once that PR is merged, the next push to `main` publishes the bumped package(s) to the registry. + +Requires two repo secrets: `NPM_TOKEN` (publish token) and `GITHUB_TOKEN` (auto-provided on GitHub Actions; use the Gitea/Forgejo equivalent if this host isn't GitHub-Actions-native — check with whoever administers `sources.vitanova.network`). + +Registry choice — pick one before first publish: +- **npm private scope** (`@marketplaces` org on npmjs.com) — simplest, works with the workflow as-is. +- **GitHub Packages** — swap `registry-url` in the workflow to `https://npm.pkg.github.com`. +- **Self-hosted (Verdaccio) on the dev server** — point `registry-url` at the server's registry endpoint; requires the registry to be stood up on `213.21.246.138` first (not done yet). + +## 4. Consuming from `marketplaces` (and other projects) + +Once published: + +```bash +npm install @marketplaces/auth @marketplaces/payment +``` + +```ts +import { ... } from '@marketplaces/auth'; +``` + +Pin exact versions (no `^`/`~` ranges) per ADR-0001's consequence about registry-outage blast radius — bump deliberately, not automatically, on this side. + +[renovate.json](../renovate.json) at repo root opens a grouped PR whenever either package publishes a new version — review and merge it manually (`automerge: false`), it does not land unattended. + +## 5. Migration cutover + +**Auth: done.** `@marketplaces/auth` now holds the real implementation — two independent modules, `telegram/` (live Telegram QR/session auth, customer + admin) and `ed25519/` (future challenge/response admin auth, backend not shipped). Environment coupling was replaced with `AUTH_API_URL`/`TELEGRAM_BOT_USERNAME` injection tokens, provided from `app.config.ts`; `environment.production` became Angular's `isDevMode()`. `AdminPermissionsService` and `requireAdminPermission` stayed in `marketplaces` (`core/admin-auth/`) since they read this app's mock Users domain, not a portable auth concern. All ~30 call sites now import `@marketplaces/auth`; the old `src/app/core/auth`, `src/app/core/admin-auth/admin-auth.service.ts` (+ interceptor, ed25519 files), `src/app/services/auth.service.ts`, `src/app/services/telegram-session-api.service.ts`, and `src/app/models/auth.model.ts`/`admin-auth.model.ts` are deleted. `npm run build`, `npm run arch:check:boundaries`, and `npm test` (103/103) all pass. Wired as a `file:packages/auth` dependency until the registry (§3) is live — swap to a real semver range once published. + +**Payment: not started.** `core/finance`/`core/pricing` still live in `marketplaces`, same process as above once prioritized. diff --git a/docs/backend/TRACK-S-SECURITY-RBAC-CONTRACT.md b/docs/backend/TRACK-S-SECURITY-RBAC-CONTRACT.md index 6220343..b8651d3 100644 --- a/docs/backend/TRACK-S-SECURITY-RBAC-CONTRACT.md +++ b/docs/backend/TRACK-S-SECURITY-RBAC-CONTRACT.md @@ -75,7 +75,35 @@ Required before: bank/payment detail changes (Phase 5 §5), production launch (P Customer/seller PII is exposed only to roles that need it for their scope (e.g. `FINANCE_VIEWER` sees payout totals, not raw bank account numbers unless `FINANCE_MANAGER`+). Export endpoints (`GET .../export`) are themselves audit-logged actions per §3. -## 8. What the frontend will start doing once this ships +## 8. Initial admin provisioning & self-service admin management + +Each marketplace ships with one bootstrap `MARKETPLACE_ADMIN` account, seeded at provisioning time (Phase 9 launch step): + +- `login` = marketplace slug (`projectName`) +- `password` = `{projectName}2026$`, flagged `mustChangePassword: true` +- Login succeeds but every non-auth request 403s with `PASSWORD_CHANGE_REQUIRED` until password is changed. + +``` +POST /api/identity/v1/session/change-password { currentPassword, newPassword } +``` + +A `MARKETPLACE_ADMIN` can then provision sub-admins scoped to their own marketplace only — mirrors the seller-team invite pattern in [Phase 5](PHASE-5-SELLER-PORTAL-CONTRACT.md) (`POST /api/seller/v1/team/invite`): + +``` +POST /api/admin/v2/team/invite { email, role: MarketplaceRole, marketplaceId } +GET /api/admin/v2/team?marketplaceId= +PATCH /api/admin/v2/team/{userId} { role } +DELETE /api/admin/v2/team/{userId} +``` + +Invariants: +- `role` must be one of the `MarketplaceRole` set (§1) — never `PlatformRole`. Backend rejects any attempt to grant a platform-scope role through this endpoint (`403 SCOPE_ESCALATION_DENIED`). +- `marketplaceId` is forced server-side to the caller's own tenant scope — request body value is ignored/validated, never trusted. +- Every invite/role-change/removal is an audit-logged action (§3, `action: 'admin_team.invited' | 'admin_team.role_changed' | 'admin_team.removed'`). +- Role grants at `MARKETPLACE_ADMIN` level require step-up auth (§6). +- Invited admins get their own credentials (email + set-password flow), not the shared bootstrap login — the bootstrap account is for first login only and should be rotated/retired once real admins exist. + +## 9. What the frontend will start doing once this ships - Route guards and action-level permission checks across the entire backoffice — currently none exist. - Backoffice **Audit & Security** section (missing from admin nav today): role changes, sensitive actions, login/security events, exports. diff --git a/docs/context/adrs/ADR-0001-extract-auth-and-payment-into-shared-marketplaces-packages.md b/docs/context/adrs/ADR-0001-extract-auth-and-payment-into-shared-marketplaces-packages.md new file mode 100644 index 0000000..979dfba --- /dev/null +++ b/docs/context/adrs/ADR-0001-extract-auth-and-payment-into-shared-marketplaces-packages.md @@ -0,0 +1,38 @@ +--- +id: ADR-0001 +title: Extract auth and payment into shared @marketplaces packages +status: active +date: 2026-08-17 +supersedes: [] +tags: [architecture, auth, payment, monorepo] +--- + +# ADR-0001: Extract auth and payment into shared @marketplaces packages + +## Context + +`marketplaces` currently owns auth end-to-end: customer auth (`core/auth` — VK ID, OTP, session, facade), admin auth (`core/admin-auth` — ed25519-verified admin sessions, permission guards, interceptor), and a legacy `services/auth.service.ts`. Payment/finance logic (`core/finance`, `core/pricing`) is server-owned per [Phase 1](../../backend/PHASE-1-MONEY-FX-PAYMENTS-CONTRACT.md) and [Phase 7](../../backend/PHASE-7-PAYMENTS-RECONCILIATION-CONTRACT.md) contracts — the frontend piece is thin (gateways/tokens, no business logic). + +Multiple marketplace projects beyond this repo need the same auth and payment client logic. Duplicating it per-project drifts fast (auth bugs get fixed in one place, not others) and blocks a consistent security posture across projects — directly relevant to [TRACK-S-SECURITY-RBAC-CONTRACT.md](../../backend/TRACK-S-SECURITY-RBAC-CONTRACT.md), which already treats auth/RBAC as the single most serious cross-cutting concern. + +## Decision + +Extract auth and payment client logic into two standalone, independently versioned npm packages: + +- `@marketplaces/auth` — customer auth (VK ID/OTP/session), admin auth (ed25519 verification, permission guards, interceptors), token/session management. +- `@marketplaces/payment` — payment/finance client gateways, FX/pricing models, checkout client contracts (thin — business logic stays backend per Phase 1/7). + +Each package: +1. Lives in its own git repo (handed over separately; this repo does not host it long-term). +2. Is consumed by `marketplaces` (and other projects) as an installed node_modules dependency — imported, never copy-pasted. +3. Is versioned with semver; CI on the package repo auto-bumps and publishes on push to `main`, driven by conventional commit prefixes already used in this repo (`feat:`/`fix:`/etc — semantic-release reads these directly). +4. Ships with its own test suite; `marketplaces` treats it as a black-box dependency, not source to edit in place. + +Rollout order: scaffold packages and CI in this repo first (reversible, local-only) → hand over target git repo → publish → migrate `marketplaces` call sites to import from the package → delete the in-repo originals only after the app builds and passes tests against the package. + +## Consequences + +- `marketplaces` loses direct edit access to auth/payment source — changes go through the package's own repo/PR/release cycle. Slower iteration, but consistent behavior across all consuming projects. +- ~30 call sites in `marketplaces` (see `core/auth`, `core/admin-auth`, `services/auth.service.ts`, interceptors) need import rewiring during migration — tracked as follow-up work, not done in this ADR. +- New failure mode: package registry/CI outage blocks `marketplaces` builds if a version bump lands mid-incident. Pin exact versions, do not use floating ranges, to keep this bounded. +- [TRACK-S-SECURITY-RBAC-CONTRACT.md](../../backend/TRACK-S-SECURITY-RBAC-CONTRACT.md) §8 (admin provisioning) becomes package-owned behavior once migrated — that doc's endpoint contracts stay backend-side and unaffected, only the frontend client implementation moves. diff --git a/docs/context/features/platform-vision/FACTS.jsonl b/docs/context/features/platform-vision/FACTS.jsonl index 49ebcf3..1b7e847 100644 --- a/docs/context/features/platform-vision/FACTS.jsonl +++ b/docs/context/features/platform-vision/FACTS.jsonl @@ -4,3 +4,4 @@ {"id":"PV-20260713T000000Z-0004","subject":"translatable-fields","predicate":"must-be-modeled-as","object":"generic translations.{lang} map so adding/removing a language automatically exposes/removes translation fields across all translatable objects","src":["docs/context/adrs/ADR-0001-marketplace-platform-vision.md"],"status":"active","kind":"constraint","updated_at":"2026-07-13T00:00:00Z","confidence":"high","tags":["i18n","constraint"]} {"id":"PV-20260713T000000Z-0005","subject":"admin-app","predicate":"is-isolated-from","object":"marketplace storefront bundle: admin code never ships to storefront and vice versa, though they may share a domain","src":["docs/context/adrs/ADR-0001-marketplace-platform-vision.md"],"status":"active","kind":"constraint","updated_at":"2026-07-13T00:00:00Z","confidence":"high","tags":["admin","security"]} {"id":"PV-20260713T000000Z-0006","subject":"widgets","predicate":"must-not-own","object":"page spacing or page width; the renderer owns sections, spacing, and page width, widgets own only their internal layout","src":["docs/context/adrs/ADR-0001-marketplace-platform-vision.md"],"status":"active","kind":"constraint","updated_at":"2026-07-13T00:00:00Z","confidence":"high","tags":["widgets","layout"]} +{"id":"PV-20260818T001500Z-a1f3","subject":"auth-and-payment-client-logic","predicate":"is-decided-to-extract-into","object":"standalone versioned npm packages @marketplaces/auth and @marketplaces/payment, installed as dependencies rather than edited in-repo","src":["docs/context/adrs/ADR-0001-extract-auth-and-payment-into-shared-marketplaces-packages.md"],"status":"active","kind":"decision","updated_at":"2026-08-18T00:15:00Z","confidence":"high","tags":["architecture","auth","payment","decision"]} diff --git a/package-lock.json b/package-lock.json index 0bc3dad..9d04560 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,6 +17,7 @@ "@angular/platform-browser": "22.0.8", "@angular/router": "22.0.8", "@angular/service-worker": "22.0.8", + "@marketplaces/auth": "file:packages/auth", "rxjs": "~7.8.0", "tslib": "^2.8.0", "zone.js": "~0.16.0" @@ -1968,6 +1969,10 @@ "win32" ] }, + "node_modules/@marketplaces/auth": { + "resolved": "packages/auth", + "link": true + }, "node_modules/@modelcontextprotocol/sdk": { "version": "1.29.0", "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.29.0.tgz", @@ -9234,6 +9239,16 @@ "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.16.0.tgz", "integrity": "sha512-LqLPpIQANebrlxY6jKcYKdgN5DTXyyHAKnnWWjE5pPfEQ4n7j5zn7mOEEpwNZVKGqx3kKKmvplEmoBrvpgROTA==", "license": "MIT" + }, + "packages/auth": { + "name": "@marketplaces/auth", + "version": "0.1.0", + "license": "UNLICENSED", + "peerDependencies": { + "@angular/common": ">=22.0.0", + "@angular/core": ">=22.0.0", + "rxjs": ">=7.8.0" + } } } } diff --git a/package.json b/package.json index d50b6a8..b2987d9 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,7 @@ }, "private": true, "dependencies": { + "@marketplaces/auth": "file:packages/auth", "@angular/animations": "22.0.8", "@angular/cdk": "22.0.6", "@angular/common": "22.0.8", diff --git a/packages/README.md b/packages/README.md new file mode 100644 index 0000000..acfa078 --- /dev/null +++ b/packages/README.md @@ -0,0 +1,11 @@ +# packages/ + +Scaffolding for `@marketplaces/auth` and `@marketplaces/payment` — see [ADR-0001](../docs/context/adrs/ADR-0001-extract-auth-and-payment-into-shared-marketplaces-packages.md). + +These are standalone-buildable packages (their own `package.json`/`tsconfig.json`), not yet wired into this repo's Angular build and not yet containing migrated code. They live here temporarily so the structure, versioning, and CI can be reviewed before: + +1. a target git repo is created for each package (or one shared repo, per ADR), +2. code moves out of `src/app/core/auth`, `src/app/core/admin-auth`, `src/app/core/finance`, `src/app/core/pricing`, +3. this repo switches to installing them as normal npm dependencies. + +See [docs/PACKAGE-EXTRACTION.md](../docs/PACKAGE-EXTRACTION.md) for the full build/version/publish/consume workflow. diff --git a/packages/auth/package.json b/packages/auth/package.json new file mode 100644 index 0000000..d4c19b8 --- /dev/null +++ b/packages/auth/package.json @@ -0,0 +1,21 @@ +{ + "name": "@marketplaces/auth", + "version": "0.1.0", + "description": "Shared customer + admin auth client (VK ID/OTP/session, ed25519 admin verification, guards, interceptors) for marketplaces projects.", + "main": "dist/index.js", + "types": "dist/index.d.ts", + "files": ["dist"], + "scripts": { + "build": "tsc -p tsconfig.json", + "test": "echo \"no tests yet\" && exit 0" + }, + "peerDependencies": { + "@angular/core": ">=22.0.0", + "@angular/common": ">=22.0.0", + "rxjs": ">=7.8.0" + }, + "publishConfig": { + "access": "restricted" + }, + "license": "UNLICENSED" +} diff --git a/packages/auth/src/config.ts b/packages/auth/src/config.ts new file mode 100644 index 0000000..f981e75 --- /dev/null +++ b/packages/auth/src/config.ts @@ -0,0 +1,7 @@ +import { InjectionToken } from '@angular/core'; + +/** Base URL for the auth backend, e.g. `https://api.example.com`. Provide from the consuming app's environment config. */ +export const AUTH_API_URL = new InjectionToken('@marketplaces/auth AUTH_API_URL'); + +/** Telegram bot username used to build QR/deep-link login URLs. Optional — falls back to a default if not provided. */ +export const TELEGRAM_BOT_USERNAME = new InjectionToken('@marketplaces/auth TELEGRAM_BOT_USERNAME'); diff --git a/src/app/core/auth/services/auth-api.service.ts b/packages/auth/src/ed25519/auth-api.service.ts similarity index 67% rename from src/app/core/auth/services/auth-api.service.ts rename to packages/auth/src/ed25519/auth-api.service.ts index a1d18c9..803eb6f 100644 --- a/src/app/core/auth/services/auth-api.service.ts +++ b/packages/auth/src/ed25519/auth-api.service.ts @@ -1,22 +1,20 @@ import { HttpClient } from '@angular/common/http'; import { Injectable, inject } from '@angular/core'; import { Observable } from 'rxjs'; -import { environment } from '../../../../environments/environment'; -import { AuthChallenge, AuthTokenPair, RefreshTokenRequest, VerifySignatureRequest } from '../models/auth-api.model'; +import { AUTH_API_URL } from '../config'; +import { AuthChallenge, AuthTokenPair, RefreshTokenRequest, VerifySignatureRequest } from './models/auth-api.model'; /** - * Thin HTTP client for the Ed25519 admin auth endpoints documented in - * docs/AUTH.md. These endpoints do not exist on the backend yet (FUTURE - - * see docs/backend/BACKEND-INTEGRATION.md §2.5) - calling them today 404s - * or connection-errors, which AuthService maps to the + * Thin HTTP client for the Ed25519 admin auth endpoints. These endpoints may + * not exist on every backend yet - calling them before the backend ships + * 404s or connection-errors, which AuthService maps to the * `backend-unavailable` error screen. No mock/fake responses are fabricated - * here; this is real HttpClient wiring against the real contract, ready for - * the moment the backend ships. + * here; this is real HttpClient wiring against the real contract. */ @Injectable({ providedIn: 'root' }) export class AuthApiService { private readonly http = inject(HttpClient); - private readonly baseUrl = `${environment.authApiUrl}/api/admin/auth`; + private readonly baseUrl = `${inject(AUTH_API_URL)}/api/admin/auth`; requestChallenge(): Observable { return this.http.get(`${this.baseUrl}/challenge`); diff --git a/src/app/core/auth/services/auth-facade.service.ts b/packages/auth/src/ed25519/auth-facade.service.ts similarity index 96% rename from src/app/core/auth/services/auth-facade.service.ts rename to packages/auth/src/ed25519/auth-facade.service.ts index e3f42d4..dc71c2e 100644 --- a/src/app/core/auth/services/auth-facade.service.ts +++ b/packages/auth/src/ed25519/auth-facade.service.ts @@ -4,7 +4,7 @@ import { finalize } from 'rxjs'; import { AuthService } from './auth.service'; import { PermissionService } from './permission.service'; import { SessionService } from './session.service'; -import { Permission } from '../models/permission.model'; +import { Permission } from './models/permission.model'; /** * Public surface for components/pages. Components should depend on this, diff --git a/src/app/core/auth/services/auth.service.ts b/packages/auth/src/ed25519/auth.service.ts similarity index 94% rename from src/app/core/auth/services/auth.service.ts rename to packages/auth/src/ed25519/auth.service.ts index ef5547f..3c7cb02 100644 --- a/src/app/core/auth/services/auth.service.ts +++ b/packages/auth/src/ed25519/auth.service.ts @@ -2,8 +2,8 @@ import { Injectable, inject, signal } from '@angular/core'; import { HttpErrorResponse } from '@angular/common/http'; import { catchError, switchMap, tap, throwError } from 'rxjs'; import { Observable } from 'rxjs'; -import { AuthTokenPair } from '../models/auth-api.model'; -import { AuthError, authErrorCodeFromBackendCode, authErrorCodeFromStatus } from '../models/auth-error.model'; +import { AuthTokenPair } from './models/auth-api.model'; +import { AuthError, authErrorCodeFromBackendCode, authErrorCodeFromStatus } from './models/auth-error.model'; import { AuthApiService } from './auth-api.service'; import { Ed25519KeypairService } from './ed25519-keypair.service'; import { SessionService } from './session.service'; @@ -18,7 +18,9 @@ export type LoginPhase = 'idle' | 'requesting-challenge' | 'signing' | 'verifyin * POST /api/admin/auth/verify -> { token, refreshToken } * * This is the lowest-level orchestrator; components should go through - * AuthFacade rather than calling this directly. + * AuthFacade rather than calling this directly. Exported from the package + * barrel as `Ed25519AuthService` to avoid colliding with the telegram + * module's `AuthService`. */ @Injectable({ providedIn: 'root' }) export class AuthService { diff --git a/src/app/core/auth/services/ed25519-keypair.service.ts b/packages/auth/src/ed25519/ed25519-keypair.service.ts similarity index 100% rename from src/app/core/auth/services/ed25519-keypair.service.ts rename to packages/auth/src/ed25519/ed25519-keypair.service.ts diff --git a/src/app/core/admin-auth/ed25519-verification.model.ts b/packages/auth/src/ed25519/ed25519-verification.model.ts similarity index 100% rename from src/app/core/admin-auth/ed25519-verification.model.ts rename to packages/auth/src/ed25519/ed25519-verification.model.ts diff --git a/src/app/core/auth/services/jwt.service.ts b/packages/auth/src/ed25519/jwt.service.ts similarity index 96% rename from src/app/core/auth/services/jwt.service.ts rename to packages/auth/src/ed25519/jwt.service.ts index 59fe7b0..ca24a08 100644 --- a/src/app/core/auth/services/jwt.service.ts +++ b/packages/auth/src/ed25519/jwt.service.ts @@ -1,5 +1,5 @@ import { Injectable } from '@angular/core'; -import { JwtClaims } from '../models/auth-api.model'; +import { JwtClaims } from './models/auth-api.model'; /** * Client-side JWT *decoding* only - never verification. The signature is diff --git a/src/app/core/auth/models/auth-api.model.ts b/packages/auth/src/ed25519/models/auth-api.model.ts similarity index 74% rename from src/app/core/auth/models/auth-api.model.ts rename to packages/auth/src/ed25519/models/auth-api.model.ts index 9f7090a..76ae54f 100644 --- a/src/app/core/auth/models/auth-api.model.ts +++ b/packages/auth/src/ed25519/models/auth-api.model.ts @@ -1,11 +1,6 @@ import { AdminRole } from './permission.model'; -/** - * Wire contracts for the Ed25519 challenge/response admin auth flow. These - * are documented in docs/AUTH.md and match the endpoints listed there - * exactly - none of this is invented beyond what's documented as FUTURE - * there and in docs/backend/BACKEND-INTEGRATION.md §2.5. - */ +/** Wire contracts for the Ed25519 challenge/response admin auth flow. */ export interface AuthChallenge { nonce: string; /** ISO 8601 issue time of the challenge. */ @@ -33,7 +28,7 @@ export interface RefreshTokenRequest { * Claims expected in the JWT `token`. Decoded client-side for display/UX * only (role-gating UI, expiry countdown) - the frontend never treats this * as proof of authorization; every admin request is still re-checked - * server-side per docs/AUTH.md security considerations. + * server-side. */ export interface JwtClaims { sub: string; diff --git a/src/app/core/auth/models/auth-error.model.ts b/packages/auth/src/ed25519/models/auth-error.model.ts similarity index 74% rename from src/app/core/auth/models/auth-error.model.ts rename to packages/auth/src/ed25519/models/auth-error.model.ts index 74a7ce0..6527791 100644 --- a/src/app/core/auth/models/auth-error.model.ts +++ b/packages/auth/src/ed25519/models/auth-error.model.ts @@ -1,7 +1,7 @@ /** * Error codes the Ed25519 admin auth flow can surface to the UI. Each maps to - * a dedicated screen (see `core/auth/pages`) rather than a generic toast, - * because the recovery action differs per code (re-login vs. retry vs. wait). + * a dedicated screen rather than a generic toast, because the recovery + * action differs per code (re-login vs. retry vs. wait). */ export type AuthErrorCode = | 'session-expired' @@ -17,12 +17,7 @@ export interface AuthError { status?: number; } -/** - * Maps the backend error envelope's `error.code` (see - * BACKEND-API-REFERENCE.md §5) to the client's AuthErrorCode screens. - * Only codes with a dedicated screen are mapped; anything else falls back - * to the HTTP-status-derived code via authErrorCodeFromStatus. - */ +/** Maps a backend error envelope's `error.code` to the client's AuthErrorCode screens. Only codes with a dedicated screen are mapped; anything else falls back to the HTTP-status-derived code via authErrorCodeFromStatus. */ const BACKEND_ERROR_CODE_MAP: Record = { TOKEN_EXPIRED: 'session-expired', INVALID_SIGNATURE: 'invalid-signature', diff --git a/src/app/core/auth/models/permission.model.ts b/packages/auth/src/ed25519/models/permission.model.ts similarity index 58% rename from src/app/core/auth/models/permission.model.ts rename to packages/auth/src/ed25519/models/permission.model.ts index 62fbb10..a4ba9a8 100644 --- a/src/app/core/auth/models/permission.model.ts +++ b/packages/auth/src/ed25519/models/permission.model.ts @@ -1,17 +1,11 @@ -/** - * Roles the Ed25519 JWT `role` claim is expected to carry (see docs/AUTH.md - * §JWT Claims). Ordered highest-to-lowest privilege; PermissionService does - * not rely on the order, it is documentation only. - */ +/** Roles the Ed25519 JWT `role` claim is expected to carry. Ordered highest-to-lowest privilege; PermissionService does not rely on the order, it is documentation only. */ export type AdminRole = 'Owner' | 'Administrator' | 'Editor' | 'Support' | 'ReadOnly'; /** - * Coarse-grained permission keys. Intentionally small and domain-agnostic - * (mirrors the existing bootstrap-level `PermissionsConfig` shape in - * `shared/models/config/permissions.model.ts`) - fine-grained, per-domain - * permissions stay server-side until the backend ships a real permission - * model; the frontend only needs enough to hide/disable UI, never to be the - * source of truth for authorization. + * Coarse-grained permission keys. Intentionally small and domain-agnostic - + * fine-grained, per-domain permissions stay server-side; the frontend only + * needs enough to hide/disable UI, never to be the source of truth for + * authorization. */ export type Permission = | 'backoffice.read' diff --git a/src/app/core/admin-auth/noop-ed25519-verification.service.ts b/packages/auth/src/ed25519/noop-ed25519-verification.service.ts similarity index 100% rename from src/app/core/admin-auth/noop-ed25519-verification.service.ts rename to packages/auth/src/ed25519/noop-ed25519-verification.service.ts diff --git a/src/app/core/auth/services/permission.service.ts b/packages/auth/src/ed25519/permission.service.ts similarity index 83% rename from src/app/core/auth/services/permission.service.ts rename to packages/auth/src/ed25519/permission.service.ts index 2f8bff9..2d46023 100644 --- a/src/app/core/auth/services/permission.service.ts +++ b/packages/auth/src/ed25519/permission.service.ts @@ -1,11 +1,11 @@ import { Injectable, computed, inject } from '@angular/core'; -import { Permission, ROLE_PERMISSIONS } from '../models/permission.model'; +import { Permission, ROLE_PERMISSIONS } from './models/permission.model'; import { SessionService } from './session.service'; /** * Derives the current admin's permission set from their JWT `role` claim. * UI-only gate (hide/disable) - the backend must independently enforce - * every mutation server-side; see docs/AUTH.md security considerations. + * every mutation server-side. */ @Injectable({ providedIn: 'root' }) export class PermissionService { diff --git a/src/app/core/auth/services/session.service.ts b/packages/auth/src/ed25519/session.service.ts similarity index 93% rename from src/app/core/auth/services/session.service.ts rename to packages/auth/src/ed25519/session.service.ts index 85536dd..e842f9f 100644 --- a/src/app/core/auth/services/session.service.ts +++ b/packages/auth/src/ed25519/session.service.ts @@ -1,5 +1,5 @@ import { Injectable, computed, signal } from '@angular/core'; -import { AuthTokenPair, JwtClaims } from '../models/auth-api.model'; +import { AuthTokenPair, JwtClaims } from './models/auth-api.model'; import { JwtService } from './jwt.service'; export type SessionStatus = 'unknown' | 'restoring' | 'authenticated' | 'unauthenticated' | 'expired'; @@ -11,9 +11,9 @@ const REFRESH_SKEW_MS = 60_000; /** * Holds the Ed25519-flow JWT/refresh-token pair and derived claims. Separate - * from AdminAuthService (Telegram-session state) by design - the two auth - * mechanisms are not merged until the backend actually ships the Ed25519 - * endpoints and a migration decision is made (see docs/AUTH.md). + * from the telegram module's AdminAuthService (Telegram-session state) by + * design - the two auth mechanisms are not merged until both ship on the + * same backend and a migration decision is made. */ @Injectable({ providedIn: 'root' }) export class SessionService { diff --git a/packages/auth/src/index.ts b/packages/auth/src/index.ts new file mode 100644 index 0000000..cc668f8 --- /dev/null +++ b/packages/auth/src/index.ts @@ -0,0 +1,43 @@ +// @marketplaces/auth — public API barrel. +// Two independent auth mechanisms, per ADR-0001 (marketplaces repo: +// docs/context/adrs/ADR-0001-extract-auth-and-payment-into-shared-marketplaces-packages.md): +// - telegram/ — live Telegram QR/session auth (customer + admin) +// - ed25519/ — future Ed25519 challenge/response admin auth (backend not shipped yet) +// Provide AUTH_API_URL (and optionally TELEGRAM_BOT_USERNAME) from the consuming app's config. + +export { AUTH_API_URL, TELEGRAM_BOT_USERNAME } from './config'; + +// Telegram module +export { AuthSession, WebSessionStart, AuthStatus, AdminAuthStatus } from './telegram/models/session.model'; +export { TelegramSessionApiService } from './telegram/telegram-session-api.service'; +export { AuthService } from './telegram/auth.service'; +export { AdminAuthService } from './telegram/admin-auth.service'; +export { adminAuthGuard } from './telegram/admin-auth.guard'; +export { adminAuthHeadersInterceptor } from './telegram/admin-auth-headers.interceptor'; + +// Ed25519 module (namespaced re-exports to avoid colliding with the telegram module's AuthService) +export { AuthService as Ed25519AuthService } from './ed25519/auth.service'; +export { AuthFacade } from './ed25519/auth-facade.service'; +export { AuthApiService } from './ed25519/auth-api.service'; +export { SessionService } from './ed25519/session.service'; +export { JwtService } from './ed25519/jwt.service'; +export { Ed25519KeypairService } from './ed25519/ed25519-keypair.service'; +export { PermissionService } from './ed25519/permission.service'; +export { + Ed25519VerificationService, + Ed25519Challenge, + Ed25519SignedResponse, + Ed25519VerificationResult +} from './ed25519/ed25519-verification.model'; +export { NoopEd25519VerificationService } from './ed25519/noop-ed25519-verification.service'; +export { + AuthChallenge, + VerifySignatureRequest, + AuthTokenPair, + RefreshTokenRequest, + JwtClaims +} from './ed25519/models/auth-api.model'; +export { AuthErrorCode, AuthError, authErrorCodeFromBackendCode, authErrorCodeFromStatus } from './ed25519/models/auth-error.model'; +export { AdminRole, Permission, ROLE_PERMISSIONS } from './ed25519/models/permission.model'; +export type { LoginPhase } from './ed25519/auth.service'; +export type { SessionStatus } from './ed25519/session.service'; diff --git a/src/app/core/admin-auth/admin-auth-headers.interceptor.ts b/packages/auth/src/telegram/admin-auth-headers.interceptor.ts similarity index 77% rename from src/app/core/admin-auth/admin-auth-headers.interceptor.ts rename to packages/auth/src/telegram/admin-auth-headers.interceptor.ts index e6a0cfb..31adb4d 100644 --- a/src/app/core/admin-auth/admin-auth-headers.interceptor.ts +++ b/packages/auth/src/telegram/admin-auth-headers.interceptor.ts @@ -2,14 +2,13 @@ import { HttpInterceptorFn } from '@angular/common/http'; import { inject } from '@angular/core'; import { AdminAuthService } from './admin-auth.service'; -/** Backend paths that require an active AdminWebSessionID per API-REFERENCE.md §0. */ +/** Backend paths that require an active AdminWebSessionID. Adjust to match your API surface if consuming this outside marketplaces. */ const ADMIN_GATED_PATH_SEGMENTS = ['/admin/', '/backoffice/', '/builder/', '/media/']; /** - * Attaches admin session/token headers only to admin API requests. Mirrors - * apiHeadersInterceptor's self-guarding pattern but scoped to admin-gated - * paths so it never touches customer requests and never reads AuthService's - * session. + * Attaches admin session/token headers only to admin API requests. Scoped to + * admin-gated paths so it never touches customer requests and never reads + * the customer AuthService's session. */ export const adminAuthHeadersInterceptor: HttpInterceptorFn = (req, next) => { const isAdminRequest = ADMIN_GATED_PATH_SEGMENTS.some(segment => req.url.includes(segment)); diff --git a/packages/auth/src/telegram/admin-auth.guard.ts b/packages/auth/src/telegram/admin-auth.guard.ts new file mode 100644 index 0000000..64bcdf7 --- /dev/null +++ b/packages/auth/src/telegram/admin-auth.guard.ts @@ -0,0 +1,15 @@ +import { inject } from '@angular/core'; +import { CanActivateFn } from '@angular/router'; +import { AdminAuthService } from './admin-auth.service'; + +/** Guards `/admin/**`-style routes. Never shares state with the customer auth guard/service. */ +export const adminAuthGuard: CanActivateFn = () => { + const adminAuth = inject(AdminAuthService); + + if (adminAuth.isAuthenticated()) { + return true; + } + + adminAuth.requestLogin(); + return false; +}; diff --git a/src/app/core/admin-auth/admin-auth.service.ts b/packages/auth/src/telegram/admin-auth.service.ts similarity index 82% rename from src/app/core/admin-auth/admin-auth.service.ts rename to packages/auth/src/telegram/admin-auth.service.ts index fa2bf67..4c30833 100644 --- a/src/app/core/admin-auth/admin-auth.service.ts +++ b/packages/auth/src/telegram/admin-auth.service.ts @@ -1,23 +1,21 @@ -import { Injectable, signal, computed, inject } from '@angular/core'; +import { Injectable, signal, computed, inject, isDevMode } from '@angular/core'; import { Observable, tap } from 'rxjs'; -import { AdminAuthStatus } from '../../models/admin-auth.model'; -import { AuthSession, WebSessionStart } from '../../models/auth.model'; -import { TelegramSessionApiService } from '../../services/telegram-session-api.service'; -import { environment } from '../../../environments/environment'; +import { AdminAuthStatus, AuthSession, WebSessionStart } from './models/session.model'; +import { TelegramSessionApiService } from './telegram-session-api.service'; /** * Admin login uses the exact same Telegram QR/session API as the customer - * login (TelegramSessionApiService, `{authApiUrl}/users/sessions`) - there is - * no separate admin backend endpoint, and none should be invented client-side. - * Only the *storage* is kept separate from AuthService, so an admin QR scan - * never authenticates the customer session or vice versa: distinct cookie - * name, distinct signals, distinct guard/interceptor. + * login (TelegramSessionApiService) - there is no separate admin backend + * endpoint, and none should be invented client-side. Only the *storage* is + * kept separate from AuthService, so an admin QR scan never authenticates + * the customer session or vice versa: distinct cookie name, distinct + * signals, distinct guard/interceptor. * - * Backend gap this creates (see docs/backend/BACKEND-INTEGRATION.md §2.5): since the session - * API itself has no concept of "admin", the frontend cannot tell an admin - * Telegram session from a regular one. Actual admin authorization must be - * enforced server-side when admin API calls are made with the resulting - * session id - the frontend only decides where to *store* the result. + * Since the session API itself has no concept of "admin", the frontend + * cannot tell an admin Telegram session from a regular one. Actual admin + * authorization must be enforced server-side when admin API calls are made + * with the resulting session id - the frontend only decides where to + * *store* the result. */ const ADMIN_SESSION_COOKIE = 'adminSessionID'; const ADMIN_TOKEN_STORAGE_KEY = 'adminToken'; @@ -93,12 +91,12 @@ export class AdminAuthService { /** * Dev-only shortcut for local testing without a reachable Telegram/session * backend: fabricates a local session and activates it directly, skipping - * the QR flow entirely. No-ops in production builds (checked at runtime, - * not just build-time, so it is safe even if this code ships). Never call - * this from anywhere reachable in a production build. + * the QR flow entirely. No-ops in production builds (checked via Angular's + * isDevMode() at runtime, not just build-time, so it is safe even if this + * code ships). Never call this from anywhere reachable in a production build. */ devBypassLogin(): void { - if (environment.production) { + if (!isDevMode()) { return; } this.hideLogin(); diff --git a/src/app/services/auth.service.ts b/packages/auth/src/telegram/auth.service.ts similarity index 96% rename from src/app/services/auth.service.ts rename to packages/auth/src/telegram/auth.service.ts index c5b321f..6703674 100644 --- a/src/app/services/auth.service.ts +++ b/packages/auth/src/telegram/auth.service.ts @@ -1,11 +1,12 @@ import { Injectable, signal, computed, inject } from '@angular/core'; import { Observable, tap } from 'rxjs'; -import { AuthSession, AuthStatus, WebSessionStart } from '../models/auth.model'; +import { AuthSession, AuthStatus, WebSessionStart } from './models/session.model'; import { TelegramSessionApiService } from './telegram-session-api.service'; const WEB_SESSION_COOKIE = 'webSessionID'; const WEB_SESSION_COOKIE_MAX_AGE_SECONDS = 60 * 60; +/** Customer-facing Telegram QR/session auth. Distinct storage/state from AdminAuthService by design. */ @Injectable({ providedIn: 'root' }) diff --git a/src/app/models/auth.model.ts b/packages/auth/src/telegram/models/session.model.ts similarity index 76% rename from src/app/models/auth.model.ts rename to packages/auth/src/telegram/models/session.model.ts index 9f8a713..a6c554b 100644 --- a/src/app/models/auth.model.ts +++ b/packages/auth/src/telegram/models/session.model.ts @@ -13,3 +13,4 @@ export interface WebSessionStart { } export type AuthStatus = 'unknown' | 'checking' | 'authenticated' | 'expired' | 'unauthenticated'; +export type AdminAuthStatus = 'unknown' | 'checking' | 'authenticated' | 'expired' | 'unauthenticated'; diff --git a/src/app/services/telegram-session-api.service.ts b/packages/auth/src/telegram/telegram-session-api.service.ts similarity index 91% rename from src/app/services/telegram-session-api.service.ts rename to packages/auth/src/telegram/telegram-session-api.service.ts index 33ac152..b697a02 100644 --- a/src/app/services/telegram-session-api.service.ts +++ b/packages/auth/src/telegram/telegram-session-api.service.ts @@ -1,11 +1,12 @@ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { Observable, of, catchError, map } from 'rxjs'; -import { AuthSession, WebSessionStart } from '../models/auth.model'; -import { environment } from '../../environments/environment'; -import { generateGuid } from '../shared/util/guid.util'; +import { AuthSession, WebSessionStart } from './models/session.model'; +import { AUTH_API_URL, TELEGRAM_BOT_USERNAME } from '../config'; +import { generateGuid } from '../util/guid.util'; const SESSION_MAX_AGE_SECONDS = 60 * 60; +const DEFAULT_TELEGRAM_BOT_USERNAME = 'DexarSupport_bot'; /** * The one Telegram QR/session API (`{authApiUrl}/users/sessions`). Customer @@ -17,9 +18,9 @@ const SESSION_MAX_AGE_SECONDS = 60 * 60; */ @Injectable({ providedIn: 'root' }) export class TelegramSessionApiService { - private readonly authApiUrl = environment.authApiUrl; - - constructor(private readonly http: HttpClient) {} + private readonly http = inject(HttpClient); + private readonly authApiUrl = inject(AUTH_API_URL); + private readonly telegramBotUsername = inject(TELEGRAM_BOT_USERNAME, { optional: true }); createSession(): Observable { const webSessionID = generateGuid(); @@ -67,7 +68,7 @@ export class TelegramSessionApiService { } private getBotUsername(): string { - return (environment as Record)['telegramBot'] as string || 'DexarSupport_bot'; + return this.telegramBotUsername || DEFAULT_TELEGRAM_BOT_USERNAME; } private normalizeWebSession(response: Record | null, fallbackSessionId: string): AuthSession | null { diff --git a/packages/auth/src/util/guid.util.ts b/packages/auth/src/util/guid.util.ts new file mode 100644 index 0000000..88ec139 --- /dev/null +++ b/packages/auth/src/util/guid.util.ts @@ -0,0 +1,21 @@ +/** RFC4122 v4-ish GUID, using crypto when available. Shared by customer and admin session creation. */ +export function generateGuid(): string { + if (globalThis.crypto?.randomUUID) { + return globalThis.crypto.randomUUID(); + } + + const bytes = new Uint8Array(16); + if (globalThis.crypto?.getRandomValues) { + globalThis.crypto.getRandomValues(bytes); + } else { + for (let index = 0; index < bytes.length; index++) { + bytes[index] = Math.floor(Math.random() * 256); + } + } + + bytes[6] = (bytes[6] & 0x0f) | 0x40; + bytes[8] = (bytes[8] & 0x3f) | 0x80; + + const hex = Array.from(bytes, byte => byte.toString(16).padStart(2, '0')); + return `${hex.slice(0, 4).join('')}-${hex.slice(4, 6).join('')}-${hex.slice(6, 8).join('')}-${hex.slice(8, 10).join('')}-${hex.slice(10, 16).join('')}`; +} diff --git a/packages/auth/tsconfig.json b/packages/auth/tsconfig.json new file mode 100644 index 0000000..c233a2a --- /dev/null +++ b/packages/auth/tsconfig.json @@ -0,0 +1,15 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "ES2022", + "moduleResolution": "bundler", + "declaration": true, + "outDir": "dist", + "rootDir": "src", + "strict": true, + "skipLibCheck": true, + "experimentalDecorators": true, + "useDefineForClassFields": false + }, + "include": ["src"] +} diff --git a/packages/payment/package.json b/packages/payment/package.json new file mode 100644 index 0000000..af18e90 --- /dev/null +++ b/packages/payment/package.json @@ -0,0 +1,21 @@ +{ + "name": "@marketplaces/payment", + "version": "0.1.0", + "description": "Shared payment/finance client (FX, pricing, checkout gateways) for marketplaces projects. Thin by design — payment business logic stays server-side per Phase 1/7 backend contracts.", + "main": "dist/index.js", + "types": "dist/index.d.ts", + "files": ["dist"], + "scripts": { + "build": "tsc -p tsconfig.json", + "test": "echo \"no tests yet\" && exit 0" + }, + "peerDependencies": { + "@angular/core": ">=22.0.0", + "@angular/common": ">=22.0.0", + "rxjs": ">=7.8.0" + }, + "publishConfig": { + "access": "restricted" + }, + "license": "UNLICENSED" +} diff --git a/packages/payment/src/index.ts b/packages/payment/src/index.ts new file mode 100644 index 0000000..aa4d0e6 --- /dev/null +++ b/packages/payment/src/index.ts @@ -0,0 +1,5 @@ +// @marketplaces/payment — public API barrel. +// Scaffold only: code migrates here from src/app/core/finance and src/app/core/pricing +// per ADR-0001 (docs/context/adrs/ADR-0001-extract-auth-and-payment-into-shared-marketplaces-packages.md). +// Nothing is exported yet — this repo still owns the live implementation until migration lands. +export {}; diff --git a/packages/payment/tsconfig.json b/packages/payment/tsconfig.json new file mode 100644 index 0000000..c233a2a --- /dev/null +++ b/packages/payment/tsconfig.json @@ -0,0 +1,15 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "ES2022", + "moduleResolution": "bundler", + "declaration": true, + "outDir": "dist", + "rootDir": "src", + "strict": true, + "skipLibCheck": true, + "experimentalDecorators": true, + "useDefineForClassFields": false + }, + "include": ["src"] +} diff --git a/renovate.json b/renovate.json new file mode 100644 index 0000000..d5e9db3 --- /dev/null +++ b/renovate.json @@ -0,0 +1,12 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "extends": ["config:recommended"], + "packageRules": [ + { + "matchPackageNames": ["@marketplaces/auth", "@marketplaces/payment"], + "groupName": "marketplaces shared packages", + "automerge": false, + "labels": ["shared-package-update"] + } + ] +} diff --git a/src/app/app.config.ts b/src/app/app.config.ts index 4572758..1304056 100644 --- a/src/app/app.config.ts +++ b/src/app/app.config.ts @@ -7,12 +7,11 @@ import { cacheInterceptor } from './interceptors/cache.interceptor'; import { apiBaseUrlInterceptor } from './interceptors/api-base-url.interceptor'; import { apiHeadersInterceptor } from './interceptors/api-headers.interceptor'; import { mockDataInterceptor } from './interceptors/mock-data.interceptor'; -import { adminAuthHeadersInterceptor } from './core/admin-auth/admin-auth-headers.interceptor'; -import { Ed25519VerificationService } from './core/admin-auth/ed25519-verification.model'; -import { NoopEd25519VerificationService } from './core/admin-auth/noop-ed25519-verification.service'; +import { adminAuthHeadersInterceptor, Ed25519VerificationService, NoopEd25519VerificationService, AUTH_API_URL, TELEGRAM_BOT_USERNAME } from '@marketplaces/auth'; import { provideServiceWorker } from '@angular/service-worker'; import { MediaRepository } from './core/media/media-repository'; import { MockMediaRepository } from './core/media/mock-media-repository.service'; +import { environment } from '../environments/environment'; export const appConfig: ApplicationConfig = { providers: [ @@ -25,6 +24,8 @@ export const appConfig: ApplicationConfig = { provideHttpClient(withXhr(), withInterceptors([mockDataInterceptor, apiBaseUrlInterceptor, apiHeadersInterceptor, adminAuthHeadersInterceptor, cacheInterceptor]) ), + { provide: AUTH_API_URL, useValue: environment.authApiUrl }, + { provide: TELEGRAM_BOT_USERNAME, useValue: environment.telegramBot }, { provide: Ed25519VerificationService, useClass: NoopEd25519VerificationService }, { provide: MediaRepository, useClass: MockMediaRepository }, provideServiceWorker('ngsw-worker.js', { diff --git a/src/app/app.routes.ts b/src/app/app.routes.ts index 523efb1..93ce6f1 100644 --- a/src/app/app.routes.ts +++ b/src/app/app.routes.ts @@ -1,7 +1,8 @@ import { Routes } from '@angular/router'; import { languageGuard } from './guards/language.guard'; import { projectEditorDirtyGuard } from './features/project-editor/guards/project-editor-dirty.guard'; -import { adminAuthGuard, requireAdminPermission } from './core/admin-auth/admin-auth.guard'; +import { adminAuthGuard } from '@marketplaces/auth'; +import { requireAdminPermission } from './core/admin-auth/admin-auth.guard'; import { authRoutes } from './core/auth/auth.routes'; import { adminCategoryDirtyGuard } from './features/admin/categories/guards/admin-category-dirty.guard'; import { adminProductDirtyGuard } from './features/admin/products/guards/admin-product-dirty.guard'; diff --git a/src/app/app.ts b/src/app/app.ts index 839a486..a12a3a9 100644 --- a/src/app/app.ts +++ b/src/app/app.ts @@ -16,8 +16,7 @@ import { UiRuntimeFacade } from './facades/runtime/ui-runtime.facade'; import { ApiHealthService } from './services/api-health.service'; import { SeoService } from './services/seo.service'; import { FloatingNotificationsComponent } from './features/website/user-experience/components/floating-notifications/floating-notifications.component'; -import { AdminAuthService } from './core/admin-auth/admin-auth.service'; -import { AuthService } from './services/auth.service'; +import { AdminAuthService, AuthService } from '@marketplaces/auth'; import { TelegramLoginComponent } from './components/telegram-login/telegram-login.component'; @Component({ diff --git a/src/app/components/header/header.component.spec.ts b/src/app/components/header/header.component.spec.ts index c425ae1..399b400 100644 --- a/src/app/components/header/header.component.spec.ts +++ b/src/app/components/header/header.component.spec.ts @@ -6,7 +6,7 @@ import { of } from 'rxjs'; import { BootstrapConfig } from '../../shared/models/config'; import { CONFIG_PROVIDER } from '../../core/config/config-provider.token'; import { ConfigService } from '../../core/config/config.service'; -import { AuthService } from '../../services/auth.service'; +import { AuthService, AUTH_API_URL } from '@marketplaces/auth'; import { HeaderComponent } from './header.component'; function makeBootstrap(): BootstrapConfig { @@ -52,6 +52,7 @@ describe('HeaderComponent profile control (login/logout gating regression)', () provideHttpClient(), provideHttpClientTesting(), { provide: CONFIG_PROVIDER, useValue: { loadBootstrap: () => of(makeBootstrap()) } }, + { provide: AUTH_API_URL, useValue: 'https://test.local' }, { provide: AuthService, useValue: fakeAuth }, ], }); diff --git a/src/app/components/header/header.component.ts b/src/app/components/header/header.component.ts index 0e4ed93..19391ff 100644 --- a/src/app/components/header/header.component.ts +++ b/src/app/components/header/header.component.ts @@ -14,7 +14,7 @@ import { FeatureConfigService } from '../../core/config/feature-config.service'; import { DEFAULT_HEADER_CONFIG, DEFAULT_USER_EXPERIENCE_CONFIG } from '../../shared/models/config'; import { StaticPageResolverService } from '../../core/config/static-page-resolver.service'; import { IconComponent } from '../../shared/ui/icon/icon.component'; -import { AuthService } from '../../services/auth.service'; +import { AuthService } from '@marketplaces/auth'; import { TelegramLoginComponent } from '../telegram-login/telegram-login.component'; @Component({ diff --git a/src/app/components/telegram-login/telegram-login.component.ts b/src/app/components/telegram-login/telegram-login.component.ts index 2b4e85e..02e624b 100644 --- a/src/app/components/telegram-login/telegram-login.component.ts +++ b/src/app/components/telegram-login/telegram-login.component.ts @@ -1,12 +1,10 @@ import { Component, ChangeDetectionStrategy, Input, Injector, Signal, inject, effect, OnDestroy, OnInit } from '@angular/core'; import { Router } from '@angular/router'; -import { AuthService } from '../../services/auth.service'; -import { AdminAuthService } from '../../core/admin-auth/admin-auth.service'; +import { AuthService, AdminAuthService, AuthSession } from '@marketplaces/auth'; import { LanguageService } from '../../services/language.service'; import { TranslatePipe } from '../../i18n/translate.pipe'; import { QrLoginEngine } from '../../shared/qr-login/qr-login.engine'; import { QrLoginAdapter, QrLoginStatus } from '../../shared/qr-login/qr-login.model'; -import { AuthSession } from '../../models/auth.model'; import { IconComponent } from '../../shared/ui/icon/icon.component'; /** diff --git a/src/app/core/admin-auth/admin-auth.guard.ts b/src/app/core/admin-auth/admin-auth.guard.ts index e7a8185..d00abab 100644 --- a/src/app/core/admin-auth/admin-auth.guard.ts +++ b/src/app/core/admin-auth/admin-auth.guard.ts @@ -1,24 +1,14 @@ import { inject } from '@angular/core'; import { CanActivateFn } from '@angular/router'; -import { AdminAuthService } from './admin-auth.service'; +import { AdminAuthService } from '@marketplaces/auth'; import { AdminPermissionsService } from './admin-permissions.service'; -/** Guards `/admin/**` routes. Never shares state with the customer auth guard/service. */ -export const adminAuthGuard: CanActivateFn = () => { - const adminAuth = inject(AdminAuthService); - - if (adminAuth.isAuthenticated()) { - return true; - } - - adminAuth.requestLogin(); - return false; -}; - /** - * UI-only gate for a specific permission, on top of adminAuthGuard's - * authentication check. See AdminPermissionsService for why this is - * cosmetic until the backend ships real admin-role enforcement. + * UI-only gate for a specific permission, on top of the package's + * adminAuthGuard authentication check. See AdminPermissionsService for why + * this is cosmetic until the backend ships real admin-role enforcement. + * Kept app-local because it depends on AdminPermissionsService, which reads + * this app's mock Users domain - not a portable auth concern. */ export function requireAdminPermission(permission: string): CanActivateFn { return () => { diff --git a/src/app/core/admin-auth/admin-permissions.service.ts b/src/app/core/admin-auth/admin-permissions.service.ts index 975916a..3acb9c6 100644 --- a/src/app/core/admin-auth/admin-permissions.service.ts +++ b/src/app/core/admin-auth/admin-permissions.service.ts @@ -1,6 +1,6 @@ import { Injectable, computed, inject } from '@angular/core'; import { toSignal } from '@angular/core/rxjs-interop'; -import { AdminAuthService } from './admin-auth.service'; +import { AdminAuthService } from '@marketplaces/auth'; import { AdminUsersLocalGateway } from '../../features/admin/users/services/admin-users-local.gateway'; /** diff --git a/src/app/core/auth/pages/admin-login-page.component.ts b/src/app/core/auth/pages/admin-login-page.component.ts index 8a661f0..f14930f 100644 --- a/src/app/core/auth/pages/admin-login-page.component.ts +++ b/src/app/core/auth/pages/admin-login-page.component.ts @@ -1,7 +1,6 @@ import { ChangeDetectionStrategy, Component, computed, inject } from '@angular/core'; import { ButtonComponent } from '../../../shared/ui/button/button.component'; -import { AuthFacade } from '../services/auth-facade.service'; -import { Ed25519KeypairService } from '../services/ed25519-keypair.service'; +import { AuthFacade, Ed25519KeypairService } from '@marketplaces/auth'; /** * Ed25519 admin login page. Prepared UI for the flow described in diff --git a/src/app/core/auth/pages/auth-error-page.component.ts b/src/app/core/auth/pages/auth-error-page.component.ts index 4bdb679..08a05c5 100644 --- a/src/app/core/auth/pages/auth-error-page.component.ts +++ b/src/app/core/auth/pages/auth-error-page.component.ts @@ -4,7 +4,7 @@ import { ActivatedRoute, Router } from '@angular/router'; import { map } from 'rxjs'; import { ButtonComponent } from '../../../shared/ui/button/button.component'; import { EmptyStateComponent } from '../../../shared/ui/empty-state/empty-state.component'; -import { AuthErrorCode } from '../models/auth-error.model'; +import { AuthErrorCode } from '@marketplaces/auth'; interface AuthErrorCopy { title: string; diff --git a/src/app/core/products/providers/api-product-data.provider.ts b/src/app/core/products/providers/api-product-data.provider.ts index b3d752f..89648d5 100644 --- a/src/app/core/products/providers/api-product-data.provider.ts +++ b/src/app/core/products/providers/api-product-data.provider.ts @@ -1,7 +1,7 @@ import { Injectable } from '@angular/core'; import { Observable, map } from 'rxjs'; import { ApiService } from '../../../services'; -import { AuthService } from '../../../services/auth.service'; +import { AuthService } from '@marketplaces/auth'; import { CategoryService } from '../../categories/category.service'; import { ProductDataProvider } from './product-data-provider.interface'; import { Product, ProductCategory, ProductListQuery, ProductListResult, ProductSearchQuery, RelatedProductsQuery } from '../models/product-domain.model'; diff --git a/src/app/features/admin/dashboard/facade/admin-dashboard.facade.ts b/src/app/features/admin/dashboard/facade/admin-dashboard.facade.ts index 37c60c9..a55ceb5 100644 --- a/src/app/features/admin/dashboard/facade/admin-dashboard.facade.ts +++ b/src/app/features/admin/dashboard/facade/admin-dashboard.facade.ts @@ -2,7 +2,7 @@ import { Injectable, computed, effect, inject, signal } from '@angular/core'; import { take } from 'rxjs/operators'; import { ProjectEditorFacade } from '../../../project-editor/facade/project-editor.facade'; import { EditorSchemaService } from '../../../project-editor/schema/editor-schema.service'; -import { AdminAuthService } from '../../../../core/admin-auth/admin-auth.service'; +import { AdminAuthService } from '@marketplaces/auth'; import { environment } from '../../../../../environments/environment'; import { ADMIN_DASHBOARD_METRICS_GATEWAY } from '../services/admin-dashboard-metrics-gateway.token'; import { AdminDashboardHistoryService } from '../services/admin-dashboard-history.service'; diff --git a/src/app/features/admin/orders/services/admin-orders-local.gateway.ts b/src/app/features/admin/orders/services/admin-orders-local.gateway.ts index cd7cd54..d1983e0 100644 --- a/src/app/features/admin/orders/services/admin-orders-local.gateway.ts +++ b/src/app/features/admin/orders/services/admin-orders-local.gateway.ts @@ -3,7 +3,7 @@ import { Observable, of } from 'rxjs'; import { delay } from 'rxjs/operators'; import { AdminOrder, AdminOrderListFilters, AdminOrdersListResult, AdminOrderStatus, TERMINAL_ORDER_STATUSES } from '../models/admin-order.model'; import { AdminOrdersGateway } from './admin-orders-gateway.interface'; -import { AdminAuthService } from '../../../../core/admin-auth/admin-auth.service'; +import { AdminAuthService } from '@marketplaces/auth'; const STATUSES: AdminOrderStatus[] = ['pending', 'processing', 'shipped', 'delivered', 'cancelled', 'refunded']; const CUSTOMER_NAMES = ['Anna Petrova', 'Karen Sargsyan', 'Ivan Ivanov', 'Mariam Grigoryan', 'Sergey Volkov', 'Lilit Hakobyan']; diff --git a/src/app/features/admin/shell/admin-layout.component.spec.ts b/src/app/features/admin/shell/admin-layout.component.spec.ts index 5423c1a..8fd7f2a 100644 --- a/src/app/features/admin/shell/admin-layout.component.spec.ts +++ b/src/app/features/admin/shell/admin-layout.component.spec.ts @@ -1,6 +1,7 @@ import { TestBed } from '@angular/core/testing'; import { provideRouter } from '@angular/router'; import { signal } from '@angular/core'; +import { AUTH_API_URL } from '@marketplaces/auth'; import { AdminLayoutComponent } from './admin-layout.component'; import { AdminOrderWatcherService } from './services/admin-order-watcher.service'; import { AdminOrder } from '../orders/models/admin-order.model'; @@ -47,6 +48,7 @@ describe('AdminLayoutComponent notifications bell', () => { imports: [AdminLayoutComponent], providers: [ provideRouter([]), + { provide: AUTH_API_URL, useValue: 'https://test.local' }, { provide: AdminOrderWatcherService, useValue: watcherStub }, ], }); diff --git a/src/app/features/admin/shell/admin-layout.component.ts b/src/app/features/admin/shell/admin-layout.component.ts index c1649f4..1049261 100644 --- a/src/app/features/admin/shell/admin-layout.component.ts +++ b/src/app/features/admin/shell/admin-layout.component.ts @@ -6,7 +6,7 @@ import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { TranslatePipe } from '../../../i18n/translate.pipe'; import { TranslateService } from '../../../i18n/translate.service'; import { LanguageService } from '../../../services/language.service'; -import { AdminAuthService } from '../../../core/admin-auth/admin-auth.service'; +import { AdminAuthService } from '@marketplaces/auth'; import { ADMIN_NAV_BOTTOM, ADMIN_NAV_PRIMARY, AdminBreadcrumbEntry, AdminNavEntry } from './admin-nav.model'; import { IconComponent } from '../../../shared/ui/icon/icon.component'; import { AdminPreferencesService } from '../settings/services/admin-preferences.service'; diff --git a/src/app/features/admin/shell/services/admin-order-watcher.service.spec.ts b/src/app/features/admin/shell/services/admin-order-watcher.service.spec.ts index 7970045..67e24d7 100644 --- a/src/app/features/admin/shell/services/admin-order-watcher.service.spec.ts +++ b/src/app/features/admin/shell/services/admin-order-watcher.service.spec.ts @@ -6,7 +6,7 @@ import { AdminOrderWatcherService } from './admin-order-watcher.service'; import { AdminOrdersLocalGateway } from '../../orders/services/admin-orders-local.gateway'; import { AdminOrder, AdminOrdersListResult } from '../../orders/models/admin-order.model'; import { UserNotificationService } from '../../../website/user-experience/services/user-notification.service'; -import { AdminAuthService } from '../../../../core/admin-auth/admin-auth.service'; +import { AdminAuthService } from '@marketplaces/auth'; function makeOrder(id: string, orderNumber: string, createdAt: string): AdminOrder { return { diff --git a/src/app/features/admin/shell/services/admin-order-watcher.service.ts b/src/app/features/admin/shell/services/admin-order-watcher.service.ts index b7509a2..12b540b 100644 --- a/src/app/features/admin/shell/services/admin-order-watcher.service.ts +++ b/src/app/features/admin/shell/services/admin-order-watcher.service.ts @@ -5,7 +5,7 @@ import { LocalStorageService } from '../../../../core/storage/local-storage.serv import { UserNotificationService } from '../../../website/user-experience/services/user-notification.service'; import { LanguageService } from '../../../../services/language.service'; import { TranslateService } from '../../../../i18n/translate.service'; -import { AdminAuthService } from '../../../../core/admin-auth/admin-auth.service'; +import { AdminAuthService } from '@marketplaces/auth'; const LAST_NOTIFIED_KEY = 'adminOrderWatcher.lastNotifiedOrderId.v1'; const LAST_NOTIFIED_AT_KEY = 'adminOrderWatcher.lastNotifiedOrderCreatedAt.v1'; diff --git a/src/app/features/admin/transactions/services/admin-transactions-local.gateway.ts b/src/app/features/admin/transactions/services/admin-transactions-local.gateway.ts index 8d1d523..7689661 100644 --- a/src/app/features/admin/transactions/services/admin-transactions-local.gateway.ts +++ b/src/app/features/admin/transactions/services/admin-transactions-local.gateway.ts @@ -4,7 +4,7 @@ import { delay } from 'rxjs/operators'; import { AdminTransaction, AdminTransactionListFilters, AdminTransactionsListResult } from '../models/admin-transaction.model'; import { AdminTransactionsGateway } from './admin-transactions-gateway.interface'; import { AdminOrdersLocalGateway } from '../../orders/services/admin-orders-local.gateway'; -import { AdminAuthService } from '../../../../core/admin-auth/admin-auth.service'; +import { AdminAuthService } from '@marketplaces/auth'; const METHODS = ['card', 'qr', 'cash_on_delivery']; diff --git a/src/app/features/admin/users/services/admin-users-local.gateway.ts b/src/app/features/admin/users/services/admin-users-local.gateway.ts index f7e2fbe..3914b51 100644 --- a/src/app/features/admin/users/services/admin-users-local.gateway.ts +++ b/src/app/features/admin/users/services/admin-users-local.gateway.ts @@ -3,7 +3,7 @@ import { Observable, of } from 'rxjs'; import { delay } from 'rxjs/operators'; import { AdminInvitation, AdminUserRoleRecord, AdminSession, AdminUser, AdminUserAuditEntry, AdminUserScope, AdminUserStatus } from '../models/admin-user.model'; import { AdminUsersGateway } from './admin-users-gateway.interface'; -import { AdminAuthService } from '../../../../core/admin-auth/admin-auth.service'; +import { AdminAuthService } from '@marketplaces/auth'; const BUILT_IN_ROLES: AdminUserRoleRecord[] = [ { id: 'owner', name: 'Owner', permissions: ['*'], builtIn: true }, diff --git a/src/app/features/search/services/search-history.service.ts b/src/app/features/search/services/search-history.service.ts index 7fb1ec6..5bb8cd6 100644 --- a/src/app/features/search/services/search-history.service.ts +++ b/src/app/features/search/services/search-history.service.ts @@ -1,5 +1,5 @@ import { Injectable, inject } from '@angular/core'; -import { AuthService } from '../../../services/auth.service'; +import { AuthService } from '@marketplaces/auth'; import { SearchHistory } from '../models/search.model'; import { BackendSearchHistoryRepository, LocalSearchHistoryRepository, SearchHistoryRepository } from './search-history.repository'; diff --git a/src/app/features/website/product/containers/product-details-container.component.ts b/src/app/features/website/product/containers/product-details-container.component.ts index 10dcdcf..a62dbff 100644 --- a/src/app/features/website/product/containers/product-details-container.component.ts +++ b/src/app/features/website/product/containers/product-details-container.component.ts @@ -21,7 +21,7 @@ import { AnalyticsService } from '../../../../core/analytics/services/analytics. import { ApiService } from '../../../../services/api.service'; import { LocalStorageService } from '../../../../core/storage/local-storage.service'; import { UserNotificationService } from '../../user-experience/services/user-notification.service'; -import { AuthService } from '../../../../services/auth.service'; +import { AuthService } from '@marketplaces/auth'; const RESTOCK_SUBSCRIPTIONS_KEY = 'restockSubscriptions'; import { ProductDeliveryInformationComponent } from '../components/delivery-information/delivery-information.component'; diff --git a/src/app/interceptors/api-headers.interceptor.ts b/src/app/interceptors/api-headers.interceptor.ts index c7d9a9c..6578617 100644 --- a/src/app/interceptors/api-headers.interceptor.ts +++ b/src/app/interceptors/api-headers.interceptor.ts @@ -3,7 +3,7 @@ import { inject } from '@angular/core'; import { ApiConfigService } from '../core/config/api-config.service'; import { LocationService } from '../services/location.service'; import { LanguageService } from '../services/language.service'; -import { AuthService } from '../services/auth.service'; +import { AuthService } from '@marketplaces/auth'; /** Map internal language codes to API header values */ const LANG_HEADER_MAP: Record = { diff --git a/src/app/layouts/containers/dynamic-page-layout.component.ts b/src/app/layouts/containers/dynamic-page-layout.component.ts index 965d10d..f1f8c3f 100644 --- a/src/app/layouts/containers/dynamic-page-layout.component.ts +++ b/src/app/layouts/containers/dynamic-page-layout.component.ts @@ -9,7 +9,7 @@ import { ResolvedWidget } from '../../widgets/contracts/widget-component.contrac import { WidgetHostService } from '../../dynamic-renderer/widget-host/widget-host.service'; import { LanguageService } from '../../services/language.service'; import { Category } from '../../core/categories/models/category-domain.model'; -import { AuthService } from '../../services/auth.service'; +import { AuthService } from '@marketplaces/auth'; @Component({ selector: 'app-dynamic-page-layout', diff --git a/src/app/models/admin-auth.model.ts b/src/app/models/admin-auth.model.ts deleted file mode 100644 index af192d9..0000000 --- a/src/app/models/admin-auth.model.ts +++ /dev/null @@ -1 +0,0 @@ -export type AdminAuthStatus = 'unknown' | 'checking' | 'authenticated' | 'expired' | 'unauthenticated'; diff --git a/src/app/models/index.ts b/src/app/models/index.ts index 2c0918a..230965f 100644 --- a/src/app/models/index.ts +++ b/src/app/models/index.ts @@ -1,4 +1,3 @@ export * from './category.model'; export * from './item.model'; export * from './location.model'; -export * from './auth.model'; diff --git a/src/app/pages/cart/cart.component.ts b/src/app/pages/cart/cart.component.ts index b23b19b..4469529 100644 --- a/src/app/pages/cart/cart.component.ts +++ b/src/app/pages/cart/cart.component.ts @@ -3,7 +3,8 @@ import { DecimalPipe } from '@angular/common'; import { Router, RouterLink } from '@angular/router'; import { FormsModule } from '@angular/forms'; import { DomSanitizer, SafeResourceUrl } from '@angular/platform-browser'; -import { CartService, ApiService, LanguageService, AuthService } from '../../services'; +import { CartService, ApiService, LanguageService } from '../../services'; +import { AuthService } from '@marketplaces/auth'; import { Item, CartItem, DeliveryOption } from '../../models'; import { EMPTY, interval, of, Subscription } from 'rxjs'; import { catchError, exhaustMap, take, timeout } from 'rxjs/operators'; diff --git a/src/app/services/index.ts b/src/app/services/index.ts index 6a18977..02d69ff 100644 --- a/src/app/services/index.ts +++ b/src/app/services/index.ts @@ -3,4 +3,3 @@ export * from './cart.service'; export * from './language.service'; export * from './seo.service'; export * from './location.service'; -export * from './auth.service';