feat/catalog-inventory #2

Merged
mihir merged 7 commits from feat/catalog-inventory into main 2026-09-10 23:43:44 +05:30
10 changed files with 79 additions and 14 deletions
Showing only changes of commit 1ed11afffe - Show all commits

View File

@ -12,3 +12,4 @@ RECOVERY_TTL_MINUTES=15
# SMTP_PASSWORD=replace_me # SMTP_PASSWORD=replace_me
# SMTP_FROM=support@example.com # SMTP_FROM=support@example.com
# RECOVERY_URL=https://shop.example.com/reset # RECOVERY_URL=https://shop.example.com/reset
DATABASE_POOL_SIZE=10

View File

@ -27,6 +27,7 @@ jobs:
node-version: '24' node-version: '24'
- run: npm install --global pnpm@11.19.0 - run: npm install --global pnpm@11.19.0
- run: pnpm install --frozen-lockfile - run: pnpm install --frozen-lockfile
- run: pnpm security:audit
- run: pnpm db:generate - run: pnpm db:generate
- run: node scripts/verify-migrations.mjs - run: node scripts/verify-migrations.mjs
- run: pnpm check - run: pnpm check

View File

@ -1,6 +1,6 @@
# Mani Candles backend # Mani Candles backend
Phase 1A provides the service foundation. Phase 1B adds staff account provisioning, authentication, sessions, recovery, configurable RBAC, and audit records. Commerce APIs follow in Phase 1C. Phase 1A provides the service foundation. Phase 1B adds staff account provisioning, authentication, sessions, recovery, configurable RBAC, and audit records. Phase 1C implements core commerce APIs.
## Setup ## Setup
@ -29,3 +29,4 @@ Development: run `pnpm dev` to compile on changes and `pnpm start:watch` in a se
Production must use a managed secret store, TLS termination, a restricted database account, backups, and the deployment migration command. Do not expose this foundation as a completed commerce platform. Production must use a managed secret store, TLS termination, a restricted database account, backups, and the deployment migration command. Do not expose this foundation as a completed commerce platform.
Identity: see [API contract](docs/identity-api.md) and [setup/release guide](docs/identity-operations.md). Identity: see [API contract](docs/identity-api.md) and [setup/release guide](docs/identity-operations.md).
Phase 1C adds catalog, private addresses and inventory. See [commerce API](docs/commerce-api.md), [errors](docs/error-contract.md), [migrations](docs/migrations.md), [security preparation](docs/vapt-readiness.md) and [verification](docs/verification.md).

27
docs/commerce-api.md Normal file
View File

@ -0,0 +1,27 @@
# Phase 1C commerce API
All routes use /api/v1. Protected routes require a bearer session and current permissions. Organization and user scope come from the session. PUT replaces editable input fields. Module schemas define the field contracts.
| Routes | Permission |
| ---------------------------------------------------------------------------------------------------------------- | --------------------------------------------------- |
| GET /products, /products/:id, /catalog-groups | catalog.read |
| POST /products, PUT /products/:id, POST /products/:id/variants, PUT /products/:id/variants/:variantId | catalog.manage |
| POST /catalog-groups, PUT /catalog-groups/:id | catalog.manage |
| PATCH /products/:id/status | catalog.publish |
| GET /storefront/:organizationId/products and /:id | Public, throttled |
| GET/POST /addresses, PUT/DELETE /addresses/:id | Authenticated, own addresses |
| GET /inventory/warehouses, /inventory/stock-items, /inventory/stock-items/:id, /inventory/stock-items/:id/ledger | inventory.read |
| POST /inventory/warehouses, /inventory/stock-items | inventory.manage |
| POST /inventory/adjustments | inventory.adjust |
| POST /inventory/reservations, GET /inventory/reservations/:id, POST /inventory/reservations/:id/release | inventory.reserve; reads/releases belong to creator |
| POST /inventory/reservations/:id/commit | inventory.commit, same organization |
Products begin as drafts. Publishing requires an active variant; archived products cannot be edited. Groups are categories or collections. Public lists contain summaries; details expose published products and active variants. Price input is a positive decimal string with two fractional digits, stored as exact Decimal(12,2) with explicit currency. Output may omit trailing zeroes. Lists and nested inputs are bounded.
Addresses are private, limited to 20 per user and have one default enforced by a partial unique index. Deleting the default selects a replacement when possible. Private address values are excluded from audits.
Stock quantities are integers. Opening stock is an adjustment. Adjustments require stockItemId, nonzero delta, reason and UUID idempotencyKey. Reservations require stockItemId, positive quantity, UUID idempotencyKey and optional ttlMinutes (160, default 15). Matching retries return the original result; changed input conflicts.
Availability equals on-hand stock minus unexpired active reservations. Stock row locks serialize changes. Expired holds stop consuming availability and cannot be committed. Release and commit are idempotent. Commit decrements stock and appends one ledger entry in the same transaction. A database trigger rejects ledger updates/deletes.
Existing system roles receive permissions through migration; custom roles require explicit grants. This phase covers finished goods. Checkout/orders and pricing snapshots follow in Phase 1D; procurement and production in Phase 2.

View File

@ -9,3 +9,4 @@ Controllers translate HTTP; services own use cases; repositories own persistence
For schema changes, run `pnpm db:migrate --name descriptive_change` against a disposable development database. Review generated SQL and commit it with the feature. Never edit an applied migration or use `db push` in production. Use expand/backfill/contract for destructive changes; back up and test recovery before deployment. Apply `pnpm db:deploy` once in the release pipeline, then verify migration status and readiness. Test migrations from an empty database and the prior release snapshot. For schema changes, run `pnpm db:migrate --name descriptive_change` against a disposable development database. Review generated SQL and commit it with the feature. Never edit an applied migration or use `db push` in production. Use expand/backfill/contract for destructive changes; back up and test recovery before deployment. Apply `pnpm db:deploy` once in the release pipeline, then verify migration status and readiness. Test migrations from an empty database and the prior release snapshot.
Money uses exact decimal or minor units with explicit currency. Inventory uses ledger entries and transactions. Orders, payments and shipments have separate states. External writes need idempotency and authenticated webhooks. Private documents require authorization and isolated processing. These rules are implemented and tested within the corresponding milestones. Money uses exact decimal or minor units with explicit currency. Inventory uses ledger entries and transactions. Orders, payments and shipments have separate states. External writes need idempotency and authenticated webhooks. Private documents require authorization and isolated processing. These rules are implemented and tested within the corresponding milestones.
See [append-only migration workflow](migrations.md) for timestamp names and checksum recording.

9
docs/error-contract.md Normal file
View File

@ -0,0 +1,9 @@
# API errors and diagnostics
Errors return `{ statusCode, code, message, requestId }`; validation errors may include fields. Clients should branch on stable codes. Definitions live in src/common/errors/platform-errors.ts and commerce-errors.ts.
Each defined error has a distinct code and message. The global filter logs the event, diagnostic, server-generated request ID, method and route template. Unexpected faults include a fingerprint. Raw exceptions, SQL, credentials, request bodies and address values are not logged or returned.
Login failures deliberately share a public message to prevent enumeration; internal diagnostics distinguish causes. Unknown failures return INTERNAL_FAILURE with a safe message. Known database failures are classified centrally.
Responses include X-Request-Id and Cache-Control: no-store. Throttled responses include Retry-After seconds. Client request IDs are not trusted. Configure restricted log access, retention and alerting at deployment.

14
docs/migrations.md Normal file
View File

@ -0,0 +1,14 @@
# Append-only migrations
Never edit or rename a committed migration. Correct mistakes with the next migration. New directories use Prisma's UTC timestamp prefix: YYYYMMDDHHmmss_description.
1. Change the relevant schema file under prisma/.
2. Run `pnpm db:migrate --name descriptive_change` against a disposable development database and review the generated SQL.
3. Run `node scripts/check-migrations.mjs --record-new`. This verifies recorded checksums before adding new entries.
4. Run `pnpm db:generate`, `pnpm db:test` and `pnpm check`. Commit schema, SQL, checksums and tests together.
The check normalizes line endings and rejects modified or missing recorded migrations and invalid new timestamp names. Review manifest changes against the base branch; replacing an old checksum is not an acceptable repair.
Production uses `pnpm db:deploy`, then `pnpm db:status`. Never use db push in production. Schema diff tooling targets the whole prisma directory. Destructive changes need an expand/backfill/contract rollout and recovery planning.
Phase 1C appends four migrations after the original three: catalog/addresses, inventory, commerce integrity and inventory actor scope. SQL maintains additional integrity constraints and the append-only ledger trigger.

View File

@ -6,7 +6,7 @@ Source: Mani Candles Commerce Platform specification and project pack created in
- 1A (implemented): service bootstrap, configuration, database lifecycle, initial organization migration, health API, test/build baseline, team workflow. - 1A (implemented): service bootstrap, configuration, database lifecycle, initial organization migration, health API, test/build baseline, team workflow.
- 1B (implemented; native CI and deployment configuration pending): authentication, sessions, recovery, users, configurable RBAC, organization access, approval status, audit events. Test denied access and cross-organization access. - 1B (implemented; native CI and deployment configuration pending): authentication, sessions, recovery, users, configurable RBAC, organization access, approval status, audit events. Test denied access and cross-organization access.
- 1C: catalog, variants, collections, addresses, inventory ledger and reservations. Test concurrent reservation and stock reconciliation. - 1C (implemented; native concurrency CI pending): catalog, variants, collections, addresses, inventory ledger and reservations. Test concurrent reservation and stock reconciliation.
- 1D: cart, checkout, orders, coupons and pricing snapshots. Test money precision, discount eligibility, retries and transaction rollback. - 1D: cart, checkout, orders, coupons and pricing snapshots. Test money precision, discount eligibility, retries and transaction rollback.
- 1E: verified payments/refunds, shipping/tracking, returns, notifications and operational dashboard. Test signatures, replay, partial fulfillment and reconciliation. - 1E: verified payments/refunds, shipping/tracking, returns, notifications and operational dashboard. Test signatures, replay, partial fulfillment and reconciliation.

13
docs/vapt-readiness.md Normal file
View File

@ -0,0 +1,13 @@
# Security assessment preparation
This phase supplies controls and regression evidence for assessment; no formal VAPT or certification has been completed.
Controls include current session/permission checks, organization-scoped lookups and composite foreign keys, private address ownership, bounded schemas, parameterized queries, security headers, explicit CORS, 32 KB bodies, durable rate limits and redacted errors/logs. Stock mutations use transactions, row locks, idempotency and an immutable ledger. Migration checksums guard history.
Tests cover denied/cross-organization access, mass assignment, malformed and oversized JSON, query bounds, hostile text, error redaction, forged request IDs and inventory state transitions. Production dependency audit reports zero advisories at verification; CI rejects high/critical findings.
Before release, run native PostgreSQL concurrency tests and Gitea CI, validate TLS and trusted-proxy configuration, test rate limits at the actual network boundary, restrict database/log access, provision secrets and backups, verify restore procedures and commission authenticated and unauthenticated VAPT against the deployed environment.
Live SMTP and the recovery frontend remain pending. Recovery delivery is synchronous until the notification queue milestone; assess timing-based enumeration with the real adapter. No external email or production deployment was performed.
References: [OWASP ASVS](https://owasp.org/www-project-application-security-verification-standard/) and [Logging Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Logging_Cheat_Sheet.html). Conformance has not been independently assessed.

View File

@ -1,16 +1,14 @@
# Verification record — Phase 1B # Verification record — Phase 1C
Completed locally: - 131 passing tests across 21 suites; two native PostgreSQL concurrency tests are skipped without TEST_DATABASE_URL.
- Coverage: 99.61% statements, 99.56% lines, 87.21% branches and 100% functions for measured application code.
- Formatting, migration checksums, Prisma validation, strict TypeScript checks and production compilation pass.
- All seven migrations execute through Prisma migrate deploy; status is up to date and schema diff reports no drift against the disposable embedded PostgreSQL engine. The original three migrations remain unchanged; four timestamped migrations were appended.
- Tests cover publication, private addresses, stock reconciliation, reservation expiry/retry/commit, append-only ledgers, organization boundaries and safe HTTP errors.
- Production dependency audit reports zero advisories after targeted transitive dependency overrides.
- 86 passing tests across 13 suites; one native PostgreSQL concurrency test is intentionally skipped without TEST_DATABASE_URL. Native PostgreSQL concurrency and remote Gitea CI remain pending. CI provisions PostgreSQL 17 for competing reservations and recovery consumption. Embedded tests do not establish multi-connection locking behavior.
- 100% statements, lines and functions; 86.11% branches for hand-written application code. Generated code, Nest module declarations and CLI/HTTP entrypoint wrappers are excluded. Bootstrap business logic is tested.
- Formatting, Prisma schema validation, strict TypeScript/unused-code checks and production compilation pass.
- All three migrations execute through Prisma migrate deploy; migrate status reports up to date and schema diff reports no drift against a disposable embedded PostgreSQL engine.
- Migration tests preserve a pre-existing organization row, reject cross-organization role assignments, enforce normalized email and one owner, and prevent audit updates/deletes.
- API tests cover pending/suspended accounts, revoked/expired sessions, current permissions, privilege escalation, recovery replay, throttling and transaction rollback.
Native PostgreSQL concurrency and the remote Gitea workflow have not been verified locally. The workflow provisions PostgreSQL 17 and enables the native test path. A Docker-capable Gitea runner and Actions access are required. No production deployment, real SMTP delivery or formal VAPT was performed. See [assessment preparation](vapt-readiness.md) for release checks.
Recovery email is tested using a mock SMTP adapter; no external email was sent. Live SMTP, a frontend recovery page and production deployment/owner bootstrap remain environment setup tasks. Recovery email is synchronous pending the later notification queue milestone. Git author: mihir <motiyanimihir@gmail.com>. Branch: feat/catalog-inventory, based on fetched main.
Git identity: mihir <motiyanimihir@gmail.com>. Work is based on the fetched main branch and lives on feat/identity-access.